LeetCode 单词的压缩编码(前缀树)

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

思 路 分 析 : \color{blue}思路分析: 这道题的题意很清晰,就是如果有一个单词的后缀是另外一个单词,则可以将这两个单词合并在一起。比如示例中的"time"和"me",第一个单词的后缀"me"是第一个单词,所有我们可以将它们合并为"time#"。

如果我们将所有的单词翻转一下,这是不是就是让我们构建一个棵前缀树?

关于前缀树请翻阅 LeetCode 实现Trie(前缀树)

//前缀树的程序表示
class TrieNode {
public:
	bool isWord;//当前节点为结尾是否是单词
	vector<TrieNode*> children;
	TrieNode() : isWord(false), children(26, nullptr) {}
	~TrieNode() {
		for (TrieNode* child : children)
			if (child) delete child;
	}
};
class Solution {
public:
    int resCount = 0;
    TrieNode *trieRoot;//构建的单词前缀树
    int minimumLengthEncoding(vector<string>& words) {
        //第一步:首先利用words构造前缀树
        trieRoot = new TrieNode();
        for (const auto &word : words){
            addWord(word);
        }
        //第二步:开始搜索树中每条路径的长度
        myFindCount(trieRoot, 1);//注意起始节点深度为1,"time#"中的字符'#'
        return resCount;
    }
    //往树中添加一个单词以及对应的权重
	void addWord(const string &word) {
		TrieNode *ptr = trieRoot;//扫描这棵树,插入word
        for (auto rit = word.rbegin(); rit != word.rend(); ++rit){
            if (ptr->children[*rit - 'a'] == NULL) {
				ptr->children[*rit - 'a'] = new TrieNode();
			}
			ptr = ptr->children[*rit - 'a'];
        }
		ptr->isWord = true;//标记为单词
	}
    //寻找以root为根的路径长度(深度)
    void myFindCount(TrieNode *trieRoot, int depth){
        bool flag = true;//如果当前trieRoot没有孩子
        for (int i = 0; i < 26; ++i){
            if (trieRoot->children[i] != nullptr){
                flag = false;
                myFindCount(trieRoot->children[i], depth + 1);
            }
        }
        if (flag){//说明它数路径终点,比如"time#"中的字符't'
            resCount += depth;
        }
    }
};

在这里插入图片描述
由于前缀树在构造树、计算所有路径长度的时候会耗费大量时间,在存储前缀树时也会耗费大量空间。
蛋式这道题也可以不需要前缀树,我们首先将words中的单词进行翻转,然后将words进行升序排序,比如示例翻转单词再排序后就是words = [“em”, “emit”, “lleb”],这样可以看出如果存在前缀关系,它们必定相邻并且后一个字符串以前一个字符串为起始。

class Solution {
public:
    int minimumLengthEncoding(vector<string>& words) {
        //第一步:将words中所有单词进行翻转
        for (auto &word : words){
            reverse(word.begin(), word.end());
        }
        //第二步:进行升序排序
        sort(words.begin(), words.end());
        int resCount = 0, wordsSize = words.size();
        //第三步:确定压缩后的长度
        for (int i = 0; i < wordsSize; ++i){
            //存在前缀关系
            if (i + 1 < wordsSize && words[i + 1].substr(0, words[i].size()) == words[i]){
                continue;
            }
            resCount += words[i].size() + 1;//注意需要添加'#'字符的长度
        }
        return resCount;
    }
};

在这里插入图片描述

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页