820. Short Encoding of Words

820. Short Encoding of Words

Question

A valid encoding of an array of words is any reference string s and array of indices indices such that:

  • words.length == indices.length
  • The reference string s ends with the '#' character.
  • For each index indices[i], the substring of s starting from indices[i] and up to (but not including) the next '#' character is equal to words[i].

Given an array of words, return the length of the shortest reference string s possible of any valid encoding of words.

Solution 1

LeetCode这几天的每日一题时不让我学会字典树势不罢休啊……

字典树,参数isLeaf记录每个TireNode节点是否为叶子节点。
将单词倒序组建字典树,并维护isLeaf的属性。

计算从根节点到达每个叶子节点的长度再加一的和即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
class TrieNode{
TrieNode[] next = new TrieNode[26];
boolean isLeaf;
}
TrieNode root;
int count;

public int minimumLengthEncoding(String[] words) {
count = 0;
root = new TrieNode();

for(String word : words){
add(word);
}
countLeaves(root, 0);
return count;
}

private void add(String word){
TrieNode curr = root;
for(int i = word.length()-1; i >= 0; i--){
int index = word.charAt(i) - 'a';
if(curr.next[index] == null){
curr.isLeaf = false;
curr.next[index] = new TrieNode();
curr.next[index].isLeaf = true;
}
curr = curr.next[index];
}
}

private void countLeaves(TrieNode root, int length){
if(root.isLeaf){
count += length + 1;
}
else{
for(int i = 0; i < 26; i++){
if(root.next[i] != null) countLeaves(root.next[i], length+1);
}
}
}
}

Solution 2

排序,将数组words根据单词倒序排序。
倒序构成字典树,如果展开新的字典树分支(创建新的TrieNode节点)则将count加入根节点到新节点的距离。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class Solution {
class TrieNode{
TrieNode[] next = new TrieNode[26];
boolean end;
}
TrieNode root;
int count;

public int minimumLengthEncoding(String[] words) {
Arrays.sort(words, new Comparator<String>(){
public int compare(String a, String b){
int i = a.length()-1, j = b.length()-1;
while(i >= 0 && j >= 0){
int index = a.charAt(i) - b.charAt(j);
if(index == 0){
i--;
j--;
}
else{
return index;
}
}
return j - i;
}
});

count = 0;
root = new TrieNode();

for(String word : words){
add(word);
}

return count;
}

private void add(String word){
TrieNode curr = root;
int length = 1;
boolean newTrieNode = false;
for(int i = word.length()-1; i >= 0; i--){
int index = word.charAt(i) - 'a';
if(curr.next[index] == null){
curr.next[index] = new TrieNode();
newTrieNode = true;
}
curr = curr.next[index];
length++;
}
curr.end = true;
if(newTrieNode) count += length;
}
}

Solution 3

倒序重组字符串并记录到reversedWords[]数组,然后根据字典顺序排序。
如果数组的上一个字符串不是下一个字符串的前缀,则加入上一个字符串的长度加一。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {    
public int minimumLengthEncoding(String[] words) {
String[] reversedWords = new String[words.length];
int count = 0, index = 0;
for(String word : words){
reversedWords[index] = new StringBuffer(word).reverse().toString();
index++;
}

Arrays.sort(reversedWords);
for(int i = 0; i < words.length-1; i++){
if(!reversedWords[i+1].startsWith(reversedWords[i])) count += reversedWords[i].length()+1;
}
count += reversedWords[words.length-1].length()+1;
return count;
}
}
1268. Search Suggestions System

1268. Search Suggestions System

Question

You are given an array of strings products and a string searchWord.

Design a system that suggests at most three product names from products after each character of searchWord is typed. Suggested products should have common prefix with searchWord. If there are more than three products with a common prefix return the three lexicographically minimums products.

Return a list of lists of the suggested products after each character of searchWord is typed.

Solution

字典树+优先级队列。
在TrieNode节点中保存PriorityQueue,以记录该节点下的三个单词。
注意PriorityQueue中的String需要按倒序排列,以保证维护时可以直接poll掉字符顺序较后的单词。

add()方法

创建并添加TrieNode节点,同时在移动当前节点位置时,需要维护PriorityQueue,当长度大于3时,则挤出字典顺序排列较后的单词。

search()方法

在查找前缀时,将每个TrieNode节点上的PriorityQueue倒序加入List。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Solution {
class TrieNode{
boolean end;
TrieNode[] children = new TrieNode[26];
PriorityQueue<String> q = new PriorityQueue<>((a, b) -> b.compareTo(a));
}

TrieNode root;

public List<List<String>> suggestedProducts(String[] products, String searchWord) {
List<List<String>> ret = new ArrayList<>();
root = new TrieNode();
for(String word : products){
add(word);
}
return search(searchWord);
}

private void add(String word){
TrieNode curr = root;
for(char c : word.toCharArray()){
int i = c - 'a';
if(curr.children[i] == null) curr.children[i] = new TrieNode();
curr = curr.children[i];
curr.q.add(word);
if(curr.q.size() > 3) curr.q.poll();
}
curr.end = true;
}

private List<List<String>> search(String prefix){
List<List<String>> ret = new ArrayList<>();
TrieNode curr = root;
for(char c : prefix.toCharArray()){
int i = c - 'a';
if(curr.children[i] == null) curr.children[i] = new TrieNode();
curr = curr.children[i];
List<String> temp = new ArrayList<>();
while(!curr.q.isEmpty()){
temp.add(curr.q.poll());
}
Collections.reverse(temp);
ret.add(temp);
}
return ret;
}
}
745. Prefix and Suffix Search

745. Prefix and Suffix Search

Question

Design a special dictionary with some words that searchs the words in it by a prefix and a suffix.

Implement the WordFilter class:

  • WordFilter(string[] words) Initializes the object with the words in the dictionary.
  • f(string prefix, string suffix) Returns the index of the word in the dictionary, which has the prefix prefix and the suffix suffix. If there is more than one valid index, return the largest of them. If there is no such word in the dictionary, return -1.

Solution

改版的前缀树,用后缀+符号+前缀的形式记录字典树。

TrieNode

用参数id记录TrieNode经历过的最大下标。

27位TrieNode数组children[]记录子TrieNode。

初始化

将words[]中的每个字符串记录在TrieNode上,保存TrieNode的根节点root。
在遍历每个字符串时,需要将每个“后缀+符号+前缀”组成的新字符串记录在字典树中,并更新当前节点的id值。

将后缀部分添加完毕后,加入特殊符号。检查并创建当前节点的curr[26]位置。
然后继续将整个前缀字符串加入字典树,并更新当前节点的id值。

搜索

搜索时,先遍历后缀,查找字典树的路径是否存在,并更新当前节点的位置,如不存在则返回-1。
然后检查并跳过curr.children[26]的位置,如不存在则返回-1。
最后遍历前缀,更新节点位置,如果路径不存在则返回-1。

最后返回当前节点的id即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class WordFilter {
class TrieNode{
int id;
TrieNode[] children;
public TrieNode(){
children = new TrieNode[27];
id = 0;
}
}

TrieNode root;
public WordFilter(String[] words) {
root = new TrieNode();

for(int k = 0; k < words.length; k++){
String word = words[k];
for(int i = 0; i < word.length(); i++){
TrieNode curr = root;
int j = i;
while(j < word.length()){
int index = word.charAt(j) - 'a';
if(curr.children[index] == null) curr.children[index] = new TrieNode();
curr = curr.children[index];
curr.id = k;
j++;
}
if(curr.children[26] == null) curr.children[26] = new TrieNode();
curr = curr.children[26];
curr.id = k;
for(char c : word.toCharArray()){
int index = c - 'a';
if(curr.children[index] == null) curr.children[index] = new TrieNode();
curr = curr.children[index];
curr.id = k;
}
}
}
}

public int f(String prefix, String suffix) {
TrieNode curr = root;
for(char c : suffix.toCharArray()){
int index = c - 'a';
if(curr.children[index] == null) return -1;
curr = curr.children[index];
}

if(curr.children[26] == null) return -1;
curr = curr.children[26];

for(char c : prefix.toCharArray()){
int index = c - 'a';
if(curr.children[index] == null) return -1;
curr = curr.children[index];
}
return curr.id;
}
}

/**
* Your WordFilter object will be instantiated and called as such:
* WordFilter obj = new WordFilter(words);
* int param_1 = obj.f(prefix,suffix);
*/
208. Implement Trie (Prefix Tree)

208. Implement Trie (Prefix Tree)

Question

A trie (pronounced as “try”) or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker.

Implement the Trie class:

  • Trie() Initializes the trie object.

  • void insert(String word) Inserts the string word into the trie.

  • boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise.

  • boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.

Solution

参考:实现 Trie :「二维数组」&「TrieNode」方式

前缀树,字典树结构实现。

前缀树 Trie

前缀树结构用“边”记录有无字符,用“点”记录单词结尾以及后续的字符串字符。
root节点记录字典的根节点。

TrieNode节点

用TrieNode节点实现前缀树。
真值end记录是否为字符串的结尾,创建时默认为false。
数组TrieNode[] children记录子节点。

insert()方法

从根节点root开始,遍历字符串。
如果对应的子节点位置为空,则创建新的TrieNode节点。
向下移动当前节点。

将最后一个节点设置为end,使得整个字符串被字典记录。

search()方法

从根节点root开始,遍历字符串。
如果子节点位置为空,则这个字符串不在字典中,返回false。
否则向下移动当前节点。

遍历完毕,如果当前节点的end为true,则存在该字符串,返回true。

startsWith()方法

从根节点root开始,遍历前缀字符串。
如果子节点位置为空,则这个前缀不在字典中,返回false。
否则向下移动当前节点。

最后返回true。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Trie {
class TrieNode {
boolean end;
TrieNode[] children = new TrieNode[26];
}
TrieNode root;
public Trie() {
root = new TrieNode();
}

public void insert(String word) {
TrieNode curr = root;
for(char c : word.toCharArray()){
int i = c - 'a';
if(curr.children[i] == null) curr.children[i] = new TrieNode();
curr = curr.children[i];
}
curr.end = true;
}

public boolean search(String word) {
TrieNode curr = root;
for(char c : word.toCharArray()){
int i = c - 'a';
if(curr.children[i] == null) return false;
curr = curr.children[i];
}
return curr.end;
}

public boolean startsWith(String prefix) {
TrieNode curr = root;
for(char c : prefix.toCharArray()){
int i = c - 'a';
if(curr.children[i] == null) return false;
curr = curr.children[i];
}
return true;
}


}

/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/