820. 单词的压缩编码
题目:
给定一个单词列表,我们将这个列表编码成一个索引字符串 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
每个单词都是小写字母 。
解答:
开始暴力法做的,但19个用例就过不去了,查也查不出来。
正确做法1:
对于一个字符串"abcdef"。如果存在另一个字符串"ef",那么ef就可以直接忽略。因为abcdef在最终字符串中是这样的:xxxx abcdef# xxxx。按照题意,ef直接可以索引到,也不用再加其他'#'号。
也就是说任意字符串,如果其后缀也出现在数组里,就可以忽略掉该后缀。
按照上面的法则对数组进行筛选,剩下的就是最终的S串,注意每个字符串要加一个'#'号。
class Solution { public: int minimumLengthEncoding(vector<string>& words) { unordered_set<string> st(words.begin(),words.end()); for(const string& s:words){ for(int i=1;i<s.size();++i){//i=1开始!!!不能把自己删了! st.erase(s.substr(i)); } } int res=0; for(const auto& s:st){ res+=s.size()+1; } return res; } };
第二个前缀树方法,有点难想:
首先是要反向思维,把单词倒序放入字典树(因为是求后缀相同的单词)。还要先对words进行排序,先把长单词放入字典树,再考虑短单词。如果不排序,后面会非常复杂,因为不知道当前单词该不该加'#'。
class trie{ public: char c; vector<trie*> next; trie():c('0') { next.resize(26); } trie(char x):c(x){ next.resize(26); } }; class Solution { public: int minimumLengthEncoding(vector<string>& words) { sort(words.begin(),words.end(),[](const string& a,const string& b){return a.size()>b.size();}); trie root; int res=0; for(const auto& s:words){ string tmp=s; reverse(tmp.begin(),tmp.end());//倒着插入字典树,因为要比较后缀,转化为比较前缀,巧妙的思路阿! trie* cur_trie=&root; bool flag=false; for(int i=0;i<tmp.size();++i){ if(cur_trie->next[tmp[i]-'a']==nullptr){ cur_trie->next[tmp[i]-'a']=new trie(tmp[i]); flag=true; cur_trie=cur_trie->next[tmp[i]-'a']; } else{ cur_trie=cur_trie->next[tmp[i]-'a']; } } res+=flag?s.size()+1:0; } return res; } };
进击的小🐴农