[LeetCode]820. 单词的压缩编码(扩展:实现 Trie (前缀树))

单词的压缩编码

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




思路

1.题目有点绕,简单说就是索引中的0,2,5分别为字符串的起始位置,#为终止位置分割S中的字符串。创建一个26位的字典树,遍历字符串数组。题中如果一个长字符串的后缀可以装下短字符串时,不统计短字符串长度,所以,倒序遍历字符串元素,如果其中每一位字符都存在则可以不统计它的长度,如果不存在,则存储该字符且统计该字符对应词的长度+1。

    class Trie {
        int val;
        Trie[] tries = new Trie[26];

        public Trie() {
        }
    }

    public int minimumLengthEncoding(String[] words) {
        //将字符串数组以字段长度倒序
        Arrays.sort(words, (o1, o2) -> o2.length() - o1.length());
        Trie trie = new Trie();
        int length = 0;
        for (String word : words) {
            boolean isNew = false;
            //不能操作头节点,因为每次都要从头节点重新遍历
            Trie cur = trie;
            //从后往前遍历,方便查找单词的后缀与后面的单词是否重合
            for (int i = word.length() - 1; i >= 0; i--) {
                //该字符在26位数组中的索引
                int c = word.charAt(i) - 'a';
                //如果当前的字符是新的,那么他会重新创建一个分支节点并延续。
                if (cur.tries[c] == null) {
                    isNew = true;
                    cur.tries[c] = new Trie();
                }
                //字典树向下遍历
                cur = cur.tries[c];
            }
            //如果是新单词,必须计算整个单词长度
            if (isNew) {
                length += word.length() + 1;
            }
        }
        return length;
    }

2.将字符串数组放入Set集合中,然后遍历字符串数组,每次都对字符串从后面截取部分,然后去Set集合中删除元素,最终Set集合留下来的肯定是后缀不包含元素。

    public int minimumLengthEncoding(String[] words) {
        //将数组放入set集合
        Set<String> good = new HashSet(Arrays.asList(words));
        //遍历删除后缀
        for (String word: words) {
            for (int k = 1; k < word.length(); ++k) {
                good.remove(word.substring(k));
            }
        }

        int ans = 0;
        //统计剩余的字符串长度
        for (String word: good) {
            ans += word.length() + 1;
        }
        return ans;
    }




拓展

实现 Trie (前缀树)

实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。所有的输入都是由小写字母 a-z 构成的。示例:

Trie trie = new Trie();

trie.insert("apple");
trie.search("apple");   // 返回 true
trie.search("app");     // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");   
trie.search("app");     // 返回 true

1.初始化一个每一个节点有26个子节点的字典树。insert时,字典树不存在则创建,然后向下遍历字典树。使用search时,前缀查询返回false,所以需要一个isEnd变量,保存单词的结束位置。

public class Trie {
    boolean isEnd;
    Trie[] tries;

    public Trie() {
        //创建一个26位的数组
        tries = new Trie[26];
    }
    
    public void insert(String word) {
        if (word.length() == 0) {
            return;
        }
        //开始时在Trie中创建并实例化了一个head节点,会一致自己创建自己导致栈溢出。。。使用this代表当前的类
        Trie aTry = this;
        for (int i = 0; i < word.length(); i++) {
            int c = word.charAt(i) - 'a';
            //如果字符不存在,则创建
            if (aTry.tries[c] == null) {
                aTry.tries[c] = new Trie();
            }
            //向下遍历字典树
            aTry = aTry.tries[c];
        }
        //单词插入完成后,在最后一个字符的位置标记上isEnd
        aTry.isEnd = true;
    }

    public boolean search(String word) {
        Trie aTry = this;
        for (int i = 0; i < word.length(); i++) {
            int c = word.charAt(i) - 'a';

            if (aTry.tries[c] == null) {
                return false;
            }
            aTry = aTry.tries[c];
        }
        //如果isEnd不为true,说明该词只是一个前缀,也返回false
        if (aTry.isEnd) {
            return true;
        }
        return false;
    }

    public boolean startsWith(String prefix) {
        Trie aTry = this;
        for (int i = 0; i < prefix.length(); i++) {
            int c = prefix.charAt(i) - 'a';
            //如果不包含字符,则返回false
            if (aTry.tries[c] == null) {
                return false;
            }
            aTry = aTry.tries[c];
        }
        return true;
    }
}
发布了36 篇原创文章 · 获赞 28 · 访问量 1505

猜你喜欢

转载自blog.csdn.net/tc979907461/article/details/105170423