成都网站网页设计/百度云资源搜索引擎入口
单词的压缩编码
给定一个单词列表,我们将这个列表编码成一个索引字符串 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];}//单词插入完成后,在最后一个字符的位置标记上isEndaTry.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,说明该词只是一个前缀,也返回falseif (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';//如果不包含字符,则返回falseif (aTry.tries[c] == null) {return false;}aTry = aTry.tries[c];}return true;}
}