给定一个单词列表,我们将这个列表编码成一个索引字符串 S
与一个索引列表 A
。
例如,如果这个列表是 ["time", "me", "bell"]
,我们就可以将其表示为 S = "time#bell#"
和 indexes = [0, 2, 5]
。
对于每一个索引,我们可以通过从字符串 S
中索引的位置开始读取字符串,直到 “#” 结束,来恢复我们之前的单词列表。
那么成功对给定单词列表进行编码的最小字符串长度是多少呢?
示例:
输入: words = ["time", "me", "bell"]
输出: 10
说明: S = "time#bell#" , indexes = [0, 2, 5] 。
提示:
1 <= words.length <= 2000
1 <= words[i].length <= 7
一开始被题目误导,以为像 me
这种后缀只会出现在 time
后面,其实顺序是可以变的,测试跑过了提交没过 >︿<,还有 indexes
其实只是帮助你理解题目意思而已,不用存储!
# 错误解法
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
ansStr = ""
ansList = []
for i in words:
# 每次都判断当前单词是否为字符串的后缀
if ansStr.endswith(i + '#'):
ansList.append(len(ansStr) - len(i) - 1)
else:
ansStr += i + '#'
ansList.append(len(ansStr))
return len(ansStr)
因为一个单词最长为 7 位,最多有 6 个后缀,可以用集合存储所有单词,枚举后缀拿去和集合对比,存在则从集合中删去
# 正确解法
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
good = set(words)
for word in words:
for k in range(1, len(word)):
good.discard(word[k:])
return sum(len(word) + 1 for word in good)
words[i]
的长度评论区 @nettee,由于要判断每个单词是否为其他单词的后缀,在最坏情况下要遍历 n 2 n^2 n2 次,但是如果将所有单词反转过来再排序,就只需要比较当前单词是否为下一个单词的前缀:
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
N = len(words)
reversed_words = []
# 反转
for word in words:
reversed_words.append(word[::-1])
# 排序
reversed_words.sort()
ans = 0
for i in range(1, N):
if (reversed_words[i].startswith(reversed_words[i-1])):
pass
else:
ans += len(reversed_words[i-1]) + 1 # 要加上一个 '#'
# 排序后的最后一个单词是没有比较的,但是它肯定不会是其他单词的前缀,直接加上
return ans + len(reversed_words[N-1]) +1
优化空间,直接用原列表存储顺序(单词不做反转,所以是判断后缀)
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
N = len(words)
words.sort(key=lambda word: word[::-1])
ans = 0
for i in range(1, N):
if (words[i].endswith(words[i-1])):
pass
else:
ans += len(words[i-1]) + 1 # 要加上一个 '#'
# 排序后的最后一个单词是没有比较的,但是它肯定不会是其他单词的前缀,直接加上
return ans + len(words[N-1]) +1
评论区 @Jejune,这个方法真是太巧妙了!
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
# 这里可以先用set来去重
words = sorted(words, key=lambda i:len(i), reverse=True)
S = ''
# 长的在前短的在后,用 # 绑定了词尾,所以有一个单词是另一个单词的后缀的话一定会命中
for word in words:
if word+'#' not in S:
S+= word+'#'
return len(S)
python没学好看不太懂。。。leetcode的官方解法
class Solution:
def minimumLengthEncoding(self, words: List[str]) -> int:
words = list(set(words)) #remove duplicates
#Trie is a nested dictionary with nodes created
# when fetched entries are missing
Trie = lambda: collections.defaultdict(Trie)
trie = Trie()
#reduce(..., S, trie) is trie[S[0]][S[1]][S[2]][...][S[S.length - 1]]
nodes = [reduce(dict.__getitem__, word[::-1], trie)
for word in words]
#Add word to the answer if it's node has no neighbors
return sum(len(word) + 1
for i, word in enumerate(words)
if len(nodes[i]) == 0)