1. 题目来源
链接:820. 单词的压缩编码
来源:LeetCode
2. 题目说明
3. 题目解析
方法一:暴力+模拟+常规解法
这是一道 Medium
问题。一开始直观上就想着能不能将这个 压缩编码串重建出来,具体解法思路如下:
- 先处理长单词,因为短单词可能是长单词的子串,给单词数组按长度排序一下就行,自己重写一个
comparator
即可 - 然后遍历排序字符串数组,对于每个单词,都在字符串进行查找
- 如果没有,直接加上这个单词,再加一个
#
号 - 如果有,就可以得到出现的位置。比如在
time#
中查找me
,得到found = 2
,然后要验证该单词后面是否紧跟着一个#号,所以我们直接访问found + word.size()
这个位置,如果不是#
号,说明不能合并,我们还是要加上这个单词和#
号。最后返回编码字符串的长度即可 - 最后就可以把压缩编码串重建,返回它的长度即可
我觉得这是第一时间该想到的方法,但是一开始看到该问题时意识到了可能是 字典树 相关,前缀树、后缀树相关,但是这方面确实欠缺,而白白空想很长时间,意义不大。
参见代码如下:
// 执行用时 :528 ms, 在所有 C++ 提交中击败了32.03%的用户
// 内存消耗 :12.6 MB, 在所有 C++ 提交中击败了83.16%的用户
class Solution {
public:
int minimumLengthEncoding(vector<string>& words) {
string str = "";
sort(words.begin(), words.end(), [](string& a, string& b){return a.size() > b.size();});
for (string word : words) {
int found = str.find(word);
if (found == string::npos || str[found + word.size()] != '#') {
str += word + "#";
}
}
return str.size();
}
};
方法二:翻转+模拟+巧妙解法
能够意识到,这个问题就是 找字符串后缀问题,那么上面采用 comparator
方法解决了。那么换一种思路,不采用 comparator
方法来处理该问题。这个思想很重要,将一个 后缀问题进行抽象简化为前缀问题,具体思路如下:
- 首先每个单词翻转,
time
变成emit
,me
变成em
, - 这样只要用默认的
sort
顺序排,就可以得到em
,emit
的顺序,那么能合并的单词就放到一起了,而且一定是当前的合并到后面一个 - 只要判读当前单词是否是紧跟着的下一个单词的前缀
- 是的话就加 0
- 不是的话就要加上当前单词的长度并再加 1,多加的 1 是
#
号 - 判断前缀的方法很简单,直接在后面的单词中
substr
取相同长度的前缀比较即可 - 由于每次都要取下一个单词,为了防止越界,只处理到倒数第二个单词,那么就要把最后一个单词的长度加入结果
res
,并再加 1 即可
参见代码如下:
// 执行用时 :112 ms, 在所有 C++ 提交中击败了78.32%的用户
// 内存消耗 :12.1 MB, 在所有 C++ 提交中击败了91.58%的用户
class Solution {
public:
int minimumLengthEncoding(vector<string>& words) {
int res = 0, n = words.size();
for (int i = 0; i < n; ++i) reverse(words[i].begin(), words[i].end());
sort(words.begin(), words.end());
for (int i = 0; i < n - 1; ++i)
res += (words[i] == words[i + 1].substr(0, words[i].size())) ? 0 : words[i].size() + 1;
return res + words.back().size() + 1;
}
};
方法三:HashSet+巧妙解法
这种方法也很巧妙,用了一个 HashSet
,将所有的单词先放到这个 HashSet
中。原理是对于每个单词,我们遍历其所有的后缀,比如 time
,那么就遍历 ime
,me
,e
,然后看 HashSet
中是否存在这些后缀,有的话就删掉,那么 HashSet
中的 me
就会被删掉,这样保证了留下来的单词不可能再合并了,最后再加上每个单词的长度到结果 res
,并且同时要加上 #
号的长度。
参见代码如下:
// 执行用时 :120 ms, 在所有 C++ 提交中击败了77.12%的用户
// 内存消耗 :15.3 MB, 在所有 C++ 提交中击败了47.37%的用户
class Solution {
public:
int minimumLengthEncoding(vector<string>& words) {
int res = 0;
unordered_set<string> st(words.begin(), words.end());
for (string word : st) {
for (int i = 1; i < word.size(); ++i) {
st.erase(word.substr(i));
}
}
for (string word : st) res += word.size() + 1;
return res;
}
};
方法四:字符串哈希+字典树+前缀树+后缀树+巧妙解法
看了看题解区大佬的解法,涉及到知识盲区了 😦😃,不过能学习到新知识也是很开心满足的。在此我不做总结,因为也不熟练,相关知识听是听说过,但没有好好使用刷过几道题,也没总结相关用法。仅贴出链接,供大家学习分享: