今天遇到一道题,不得不说,思路决定一切,分享一下我的解题心路历程和对大佬的解题方式的解析与感想。
820. 单词的压缩编码
题目来源:https://leetcode-cn.com/problems/short-encoding-of-words/
给定一个单词列表,我们将这个列表编码成一个索引字符串 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
每个单词都是小写字母 。
言简意赅的解释一下题目要求:比如说time和me ,因为me是time的后缀 所以要去掉,因为可以从time#中找到me#,不需要再次建立time#me#,这样就形成了个最短的字符串长度。
我的思路: 一开始建立一个集合(目的去重),将words中的字符串存入集合中,然后访问集合,删掉当前字符串的后缀。最后加上集合中(所有字符串的长度+1),就是最终的最短长度。写出来后虽然过了但是运行比较慢,看了官方解析后将代码优化如下(发现居然可以用substr来删后缀,还是不熟练呀)。
当然,这题也可以用字典树来解决,不过空间复杂度会比较大(其实是喜欢代码简洁)。
class Solution { public: int minimumLengthEncoding(vector<string>& words) { int len = 0; unordered_set<string> S(words.begin(),words.end()); for(const string& word:words) for(int i = 1 ; i < word.size() ; i++) S.erase(word.substr(i)); for(auto word : S) len+=word.size() + 1; return len; } };
复杂度较高,为指数级。
下面分享一下大佬思路(惊艳到了)。
首先无论是什么语言,排序字符串的容器总是按照字典序来的,那么我们可以发现一个规律,将所有字符串倒序,例如:time -> emit me->em dell->lled,此时我们再排序,是不是em 一定在emit前面,我们是不是就可以更加轻松的找到em,并且删除。无论任何字符串,都可以按照字典序来一一排除,最后剩下没有存在后缀的字符串。
大佬代码:
class Solution { public: int minimumLengthEncoding(vector<string>& words) { for(auto &s : words){ reverse(s.begin(),s.end()); } sort(words.begin(),words.end()); int res=0; for(int i=0;i<words.size()-1;i++){ int size=words[i].size(); if(words[i]==words[i+1].substr(0,size)) continue; res+=size+1; } return res+words.back().size()+1; } };
只能说思路太强。
打卡:遇到字符串去重去后缀,首先思考集合,字典树,然后反转字典序排序。