单词的压缩编码--前缀树的灵活运用

0x01.问题

给定一个单词列表,我们将这个列表编码成一个索引字符串 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 每个单词都是小写字母 。

C++函数形式为:    int minimumLengthEncoding(vector<string>& words) 

此题来源于Leetcode

0x02.详细分析

第一步,读懂题意,大概含义如下:

  • 将一串单词用索引字符串和索引列表的形式表示,求字符串的最小长度。
  • 具有相同后缀的单词可以在索引字符串中用同最长的那个单词表示,只是索引列表记录的开始值不同而已。

第二步,提取出核心问题:

  • 核心问题就是将具有相同后缀的单词合并为 具有相同后缀且长度最长的那个单词存入存入一个字符串,若没有相同后缀则直接存入,最后返回存入的每个单词长度+1的总和(包含一个#
  • 再具体一点,保留所有不是其它单词后缀的单词。

第三步,思路分析:

第一种思路,也是最容易想到的,先把所有的单词存入哈希表,然后遍历每个单词,再穷举它可能的后缀,如果存在哈希表中,就删去,最后统计哈希表中的单词长度得到答案。

  • 将所有单词存入哈希表。
  • 枚举每个单词可能的后缀。
  • 如果存在哈希表中,就删去。
  • 统计哈希表中剩余单词的长度。

这种方法是完全可行的,但这是建立在单词长度有限条件,如果单词太长,效率就会比较低。

第二种思路,一看,需要大量的操作前缀,我们就想到可以运用前缀树,但,这是后缀呀,没关系,我们把字符串翻转过来存入就行了。现在我们来确定一下具体的思路, 对于每个单词,倒序存入前缀树,如果开辟了新的节点,用count++表示开辟了一个新的节点,然后用一个哈希表存储你每个单词的最后一个节点和对于单词的下标,如果它的最后一个节点中的count=0说明,这个节点是一个单词的最后一个节点,并且没有其它单词在创建的过程中遇到这个节点,那么,这个单词就是不是其它单词前缀的单词(反转后的),我们就可以统计这些单词的长度。
首先改造前缀树的思路:

  • 设立一个count,用来记录这个节点开辟了下一个节点的数目。
  • 改造一个get函数用来得到一个单词的下一个节点,如果开辟了新的节点,计数。

整体思路:

  • 遍历每个单词,存入前缀树,
  • 用一个哈希表来存储一个单词的末尾节点和对应单词数组中的下标。
  • 对每个末尾节点的count0的节点计数。

0x03.解决代码–方法一

int minimumLengthEncoding(vector<string>& words) {
    unordered_set<string> result(words.begin(),words.end());
    for(const string &word:words){
        for(int i=1;i<word.size();i++){
            result.erase(word.substr(i));
        }
    }
    int ans=0;
    for(const string &word:result){
        ans+=word.size()+1;
    }
    return ans;
}

0x04.解决代码–方法二:前缀树

class Trie{
private:
    Trie*next[26];
public:
    int count;
    Trie(){
        count=0;
        memset(next,0,sizeof(next));
    }

    Trie*get(char c){
        Trie*node=this;
        if(node->next[c-'a']==NULL){
            node->next[c-'a']=new Trie();
            count++;
        }
        return node->next[c-'a'];
    }
};
class Solution {
public:
    int minimumLengthEncoding(vector<string>& words) {
        Trie*TrieHead=new Trie();
        unordered_map<Trie*,int> map;
        for(int i=0;i<words.size();i++){
            string s=words[i];
            Trie*curr=TrieHead;
            for(int j=words[i].size()-1;j>=0;j--){
                curr=curr->get(words[i][j]);
            }
            map[curr]=i;
        }
        int ans=0;
        for(auto&[node,index]:map){
            if(node->count==0){
                ans+=words[index].size()+1;
            }
        }
        return ans;
    }
};

ATFWUS --Writing By 2020–03–28

你可能感兴趣的:(算法)