題目地址:
https://www.lintcode.com/problem/short-encoding-of-words/description
給定一個字符串列表,現在給出一個編碼方式,例如對於["time", "me", "bell"]
來說,可以如下進行編碼:編碼由兩部分組成,一部分是字符串S = "time#bell#"
,另一部分是個數組[0, 2, 5]
,解碼的時候只需要從數組中取出一個數字作爲起始下標,然後從S
的這個下標開始向後走到#
即得到一個字符串。例如從下標爲0
開始走到#
得到的是time
,下標爲2
開始走到#
得到的是me
。這樣就可以還原出所有的字符串。很顯然編碼方式是不唯一的,並且有可能對應不同的字符串。要求求出所有編碼方式中可以得到的最短的S
的長度是多少。
要得到最短的S
只需要最大程度地複用字符串後綴即可。由此想到用Trie,每個字符串從後向前遍歷加入Trie中,這樣就複用了後綴,然後再用DFS求一下所有葉子節點的深度之和加(加是因爲#
也佔一個位置)即可。代碼如下:
public class Solution {
class Trie {
class Node {
Node[] nexts;
Node() {
nexts = new Node[26];
}
}
Node root;
public Trie() {
root = new Node();
}
public void insert(String s) {
Node cur = root;
// 加單詞的時候要逆序遍歷加進去
for (int i = s.length() - 1; i >= 0; i--) {
char c = s.charAt(i);
if (cur.nexts[c - 'a'] == null) {
cur.nexts[c - 'a'] = new Node();
}
cur = cur.nexts[c - 'a'];
}
}
public int dfs() {
return dfs(root, 0);
}
private int dfs(Node root, int depth) {
int res = 0;
boolean isLeaf = true;
for (int i = 0; i < root.nexts.length; i++) {
if (root.nexts[i] != null) {
isLeaf = false;
res += dfs(root.nexts[i], depth + 1);
}
}
// 如果走到了葉子,則返回深度 + 1,否則累加所有分叉的(葉子深度 + 1)然後返回
if (isLeaf) {
return depth + 1;
} else {
return res;
}
}
}
/**
* @param words:
* @return: nothing
*/
public int minimumLengthEncoding(String[] words) {
Trie trie = new Trie();
// 先將所有單詞加入trie
for (String word : words) {
trie.insert(word);
}
return trie.dfs();
}
}
時空複雜度,爲Trie的節點個數。