问题
给定一个单词列表,我们将这个列表编码成一个索引字符串 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:nt方法(我的方法,超时)
class Solution {
public:
int minimumLengthEncoding(vector& words) {
int res = 0;
for (int i=0; i
for (int j=i+1; j
if (words[i] == "0") break;
if (words[j] == "0") continue;
if (words[i].back() != words[j].back()) continue; // 剪枝
if (words[i].size() > words[j].size()) {
if (words[j] == words[i].substr(words[i].size() - words[j].size()))
words[j] = "0";
}
else {
if (words[i] == words[j].substr(words[j].size() - words[i].size()))
words[i] = "0";
}
}
}
for (const auto& i : words) {
if (i != "0") {
res += i.size() + 1;
}
}
return res;
}
};
重点思路
时间复杂度O(N^2),用substr的方法切后缀,后缀长度为上一个字符串的长度。若和后缀相等则合并,并设为0,功能等同于erase。最后再统计words中所有不为0的字符串总长度。
解答2:字符串翻转+排序(占用内存少,方法泛用性不高)
class Solution {
public:
int minimumLengthEncoding(vector& words) {
int res = 0;
for (auto& i : words) {
reverse(i.begin(), i.end());
}
sort(words.begin(), words.end());
for (int i=0; i
if (words[i].size() > words[i+1].size() || words[i] != words[i+1].substr(0, words[i].size()))
res += words[i].size() + 1;
}
return res + words.back().size() + 1;
}
};
重点思路
首先翻转words中字符串,再使用sort排序。sort对字符串的默认排序方法是先比较字母大小,再比较长短,字母小的短的排在前面。翻转排序后,只需要比较相邻两个字符串即可。
解答3:前缀树、字典树、Trie方法(占用内存多,方法泛用性高)
class Trie {
private:
Trie* next[26] {nullptr};
public:
bool is_leaf = 1;
Trie() {}
Trie* insert(string s) {
Trie* root = this;
for (int i=s.size()-1; i>=0; i--) {
if (!root->next[s[i] - 'a']) {
root->next[s[i] - 'a'] = new Trie();
root->is_leaf = 0;
}
root = root->next[s[i] - 'a'];
}
return root;
}
};
class Solution {
public:
int minimumLengthEncoding(vector& words) {
int res = 0;
unordered_map ump; // 总个数为words中字符串个数
Trie* t = new Trie();
for (int i=0; i
ump[t->insert(words[i])] = i;
}
for (const auto& [n, i] : ump) {
if (n->is_leaf) {
res += words[i].size() + 1;
}
}
return res;
}
};
重点思路
由于该题需要大量运用字符串的前缀or后缀,很容易想到Trie方法。由于Trie为前缀树,所以放入Trie时使用倒序。本题使用所有叶子结点处所代表的字符串总和,故使用一个哈希表存放words中每个字符串是否含有叶子节点。本题的ump中,键中存放倒序后的每个字符串最后一个字母所在的节点,该节点含is_leaf成员变量来表示是否为叶子结点,键值存放该节点所在的字符串在words中对应的下标。
最后,遍历ump,将存在叶子结点的字符串全部长度相加,再加上对应的#即可。