最短独占单词缩写

Submission

运行时间: 68 ms

内存: 16.3 MB

class Solution:
    def minAbbreviation(self, target: str, dictionary: List[str]) -> str:
        ans = None
        mmin = None

        def getAbbr(mask, target):
            ans = ''
            num = 0
            prev = None
            for i in range(len(target)):
                if mask & (1 << i):
                    if prev != None:
                        ans += str(i - prev)
                        num += 1
                        prev = None
                    ans += target[i]
                    num += 1
                else:
                    if prev == None:
                        prev = i
            if prev:
                ans += str(len(target) - prev)
                num += 1
            return ans, num

        masks = set()
        for word in dictionary:
            if len(target) == len(word):
                mask = 0
                for i in range(len(target)):
                    if target[i] != word[i]:
                        mask += (1 << i)
                masks.add(mask)
        if len(masks) == 0:
            return str(len(target))
        for i in range(2 ** len(target)):
            if all(i & mask > 0 for mask in masks):
                abbr, length = getAbbr(i, target)
                if ans == None or length < mmin:
                    ans = abbr
                    mmin = length
        return ans

Explain

该题解使用位掩码来枚举target字符串的所有可能的缩写形式。对于字典中的每个单词,计算它与target的位掩码差异,并将差异掩码存储在一个集合中。然后枚举target的所有可能的位掩码,检查每个掩码是否能够区分target和字典中的所有单词。在所有有效的掩码中,选择对应的缩写长度最短的那个作为最终答案。

时间复杂度: O(mn + m2^n)

空间复杂度: O(m + n)

class Solution:
    def minAbbreviation(self, target: str, dictionary: List[str]) -> str:
        ans = None
        mmin = None

        def getAbbr(mask, target):
            ans = ''
            num = 0
            prev = None
            for i in range(len(target)):
                if mask & (1 << i):
                    if prev != None:
                        ans += str(i - prev)
                        num += 1
                        prev = None
                    ans += target[i]
                    num += 1
                else:
                    if prev == None:
                        prev = i
            if prev:
                ans += str(len(target) - prev)
                num += 1
            return ans, num

        # 生成字典中单词的差异掩码
        masks = set()
        for word in dictionary:
            if len(target) == len(word):
                mask = 0
                for i in range(len(target)):
                    if target[i] != word[i]:
                        mask += (1 << i)
                masks.add(mask)
        
        # 如果字典为空,直接返回target的长度作为缩写
        if len(masks) == 0:
            return str(len(target))
        
        # 枚举target的所有可能的位掩码
        for i in range(2 ** len(target)):
            # 检查当前掩码是否能够区分target和字典中的所有单词
            if all(i & mask > 0 for mask in masks):
                abbr, length = getAbbr(i, target)
                if ans == None or length < mmin:
                    ans = abbr
                    mmin = length
        return ans

Explore

在题解中,位掩码是一种技巧,用来表示字符串的每个字符是否被省略或保留。每个位(0或1)代表一个字符:'1'表示保留字符,'0'表示省略字符。例如,对于字符串'target',掩码'101010'(二进制表示)意味着保留第1、3、5个字符,省略第2、4、6个字符,从而形成缩写。枚举所有可能的位掩码等同于枚举target的所有可能缩写形式。通过对0到2^len(target)-1的所有整数进行循环,我们可以生成所有可能的掩码,并用这些掩码来构建target的每一种缩写形式。

条件`i & mask > 0`确保掩码`i`在与字典中每个单词对应的掩码`mask`进行按位与操作后,结果不为零。这意味着至少存在一个位置,在该位置上,target和字典中的单词在掩码`i`中是被保留的(即对应位是1),而在单词掩码`mask`中是不同的(单词在该位置的字符与target不同)。这保证了掩码`i`能够区分target和字典中的该单词。更直观的理解是:如果掩码`i`至少在一个与字典单词不同的字符位置上为1,那么这个掩码就能帮助区分target和该字典单词。

在`getAbbr`函数中,`prev`变量用来记录上一个被省略字符的位置。当遇到一个要保留的字符时(即掩码在该位置是1),如果`prev`不为空(即之前有省略的字符),则计算从`prev`位置到当前字符位置之间的字符数,这个数值将被添加到缩写中。例如,如果`prev`是2,当前位置是5,那么在缩写中添加'3'表示这两个位置之间省略了3个字符。此后,`prev`被重置为null,直到遇到下一个省略的字符序列,再次开始计数。如果`prev`非空且遍历完所有字符,还需要添加从`prev`到字符串末尾的省略字符数目。