## Sunday, May 15, 2016

### LIKE CODING: MJ [56] Longest String Chain

LIKE CODING: MJ [56] Longest String Chain
Given a set of strings, find the longest string chain. Two strings are chained if
1. both strings belong to the given set
2. the second string is generated  by remove one letter in the first string
For example:
Given vector<string> w = {a,ba,bca,bda,bdca}, the longest string chain is bdca->bda->ba->a.

Solution:
Represent the string chain by a Trie like structure. Record the depth while building Trie.

bdca
|      |
bda    bca
|        |
ba      ba
|
a
`class TrieNode{`
`public:`
`    ``string key;`
`    ``int depth = 0;`
`    ``unordered_set<TrieNode*> children;`
`    ``TrieNode(string k):key(k){};`
`    ``TrieNode(){};`
`};`
`class LongChain{`
`public:`
`    ``unordered_map<string, TrieNode*> hash;`
`    ``unordered_set<string> dictset;`
`    ``void extend(TrieNode* root){`
`        ``string w = root->key;`
`        ``for``(int i=0; i<(int)w.size(); ++i){`
`            ``string wt = w;`
`            ``wt.erase(wt.begin()+i);`
`            ``if``(dictset.count(wt)){`
`                ``if``(hash.count(wt)==0){`
`                    ``hash[wt] = ``new` `TrieNode(wt);`
`                    ``extend(hash[wt]);`
`                ``}`
`                ``root->children.insert(hash[wt]);`
`            ``}`
`        ``}`
`    ``}`
`    ``void build(vector<string> dict){`
`        ``for``(auto w:dict) dictset.insert(w);`
`        ``for``(auto w:dict){`
`            ``if``(hash.count(w)==0){`
`                ``hash[w] = ``new` `TrieNode(w);`
`                ``extend(hash[w]);`
`            ``}`
`        ``}`
`    ``}`
`    ``int getMaxLen(vector<string> dict){`
`        ``build(dict);`
`        ``int ret = 0;`
`        ``for``(auto w:dict){`
`            ``ret = max(ret, getMaxLen(hash[w]));`
`        ``}`
`        ``return` `ret;`
`    ``}`
`    ``int getMaxLen(TrieNode *node){`
`        ``if``(node->depth==0){`
`            ``if``(node->children.empty()){`
`                ``node->depth = 1;`
`            ``}``else``{`
`                ``int len = 0;`
`                ``for``(auto c:node->children){`
`                    ``len = max(len, getMaxLen(c));`
`                ``}`
`                ``node->depth = len+1;`
`            ``}`
`        ``}`
`        ``return` `node->depth;`
`    ``}`
`    ``void print(){`
`        ``for``(auto w:dictset){`
`            ``if``(hash.count(w)) print(hash[w], ``""``);`
`        ``}`
`    ``}`
`    ``void print(TrieNode *node, string ss){`
`        ``string w = node->key;`
`        ``cout<<ss<<w<<endl;`
`        ``hash.erase(w);`
`        ``for``(auto n:node->children){`
`            ``if``(hash.count(n->key)) print(hash[n->key], ss+``"----"``);`
`        ``}`
`    ``}`
`};`
https://github.com/xieqilu/Qilu-leetcode/blob/master/B218.LongestChain.cs
This problem is very similiar to Word Ladder, the methof is almost the same. We can view
this problem as a tree problem and each word in the input string array can be the root. Starting
from each root, we perform the BFS Level Order traverse, remove each char from root to check all
possible children for next level. And whenver we hit a string that only has one char, we find the
longest chain and return depth. If we never hit a string that only has one char, then we will search
to the deepest level as we can, and return the largest depth.
Solution:
Differences from the solution for Word Ladder:
1, we need to construct a dictionary for all words in input string array to enable quick look-up.
2, each word in the input array can be the root, so we need to traverse the array and try each word
as the root.
3, Use variable res to save current longest length. If a word in the array is shorter than or equal
to res, no need to try BFS on it, because from this word we cannot find a longer length. Or when the
length of the word is 1, dont't do BFS, too.
4, In the Bfs method, we don't need to use the HashSet visited to store all visited words. Because
in this problem the length of a child word must be shorted than its parent, so a child will never
point reversely to its parent, thus we don't need to worry about a cycle. So this problem is excately
a tree problem in which only parent can points to child. The word ladder problem is more like a
graph problem in which two nodes can point to each other.
5, There are two situations in the Bfs method to return depth:
5.1, we hit a child that has only one char, then we immediately return depth. Because the child belongs to the next level, so return depth+1.
5.2, we got no valid child at a level thus the search is terminated, then we must return depth-1
because we terminate the search at the current empty level, so the chain actually ends at the last
level.
Time complexity: O(n^2)  n is the number of words in input array
Use each word as the root, in each pass, we will at most search for all n words, so O(n^2)
Space: O(n)  Each search we will at most store all words in the queue

We can first sort word by length, so we can exit early.
public static int FindLongestChain(string[] words){
int len = words.Length;
HashSet<string> dict = new HashSet<string>();
foreach(string s in words)
int res = 0;
foreach(string s in words){
//if the length of s is less or equal to res,
//then it's not possible to find a longer res starting from s
if(s.Length<=res || s.Length==1)
continue;
res = Math.Max(res,Bfs(s, dict));
}
return res;
}

//Find the length of longest chain starting from s
private static int Bfs(string s, HashSet<string> dict){
Queue<string> q = new Queue<string>();
int current = 1, next = 0;
int depth = 1; //If no chain, the length is 1
q.Enqueue(s);
while(current!=0){
string currWord = q.Dequeue();
current--;
for(int i=0;i<currWord.Length;i++){
StringBuilder sb = new StringBuilder(currWord);
sb.Remove(i,1);
string temp = sb.ToString();
if(dict.Contains(temp)){
if(temp.Length==1){
return depth+1; //the chain ends at next level, so depth++
}
q.Enqueue(temp);
next++;
}
}
if(current==0){
current = next;
next = 0;
depth++;
}
}
//if no return inside while loop, chain ends at last level, so return depth-1.
return depth-1;
}
http://buttercola.blogspot.com/2015/10/zenefits-oa-longest-chain.html

`    ``private` `static` `int` `max = ``0``;`
`    ``public` `static` `void` `main(String[] args) {`
`        ``Scanner scanner = ``new` `Scanner(System.in);`
`        ``int` `n = scanner.nextInt();`
`        ``String[] dict = ``new` `String[n];`
`        `
`        ``for` `(``int` `i = ``0``; i < n; i++) {`
`            ``dict[i] = scanner.next();`
`        ``}`
`        `
`        ``int` `result = longestWordChain(dict);`
`        `
`        ``System.out.println(result);`
`        `
`        ``scanner.close();`
`    ``}`
`    `
`    ``public` `static` `int` `longestWordChain(String[] dict) {`
`        ``if` `(dict == ``null` `|| dict.length == ``0``) {`
`            ``return` `0``;`
`        ``}`
`        `
`        ``Map<Integer, Set<String>> map = ``new` `HashMap<>();`
`        `
`        ``// Fill the map`
`        ``int` `maxLen = ``0``;`
`        `
`        ``for` `(String token : dict) {`
`            ``int` `len = token.length();`
`            ``if` `(map.containsKey(len)) {`
`                ``map.get(len).add(token);`
`            ``} ``else` `{`
`                ``Set<String> words = ``new` `HashSet<>();`
`                ``words.add(token);`
`                ``map.put(len, words);`
`            ``}`
`            `
`            ``maxLen = Math.max(maxLen, len); `
`        ``}`
`        `
`        ``int` `result = longestWordChainHelper(maxLen, ``1``, map);`
`        `
`        ``return` `result;`
`    ``}`
`    `
`    ``private` `static` `int` `longestWordChainHelper(``int` `curLen, ``int` `level, `
`        ``Map<Integer, Set<String>> dict) {`
`        ``if` `(curLen == ``0``) {`
`            ``return` `max;`
`        ``}`
`        `
`        ``if` `(dict.containsKey(curLen)) {`
`            ``Set<String> words = dict.get(curLen);`
`            ``Iterator<String> it = words.iterator();`
`            ``while` `(it.hasNext()) {`
`                ``String s = it.next();`
`                ``for` `(``int` `i = ``0``; i < s.length(); i++) {`
`                    ``StringBuffer sb = ``new` `StringBuffer(s);`
`                    ``sb.deleteCharAt(i);`
`                    ``String nextStr = sb.toString();`
`                    ``if` `(dict.containsKey(curLen - ``1``) && `
`                        ``dict.get(curLen - ``1``).contains(nextStr)) {`
`                        ``longestWordChainHelper(curLen - ``1``, level + ``1``, dict);`
`                    ``}`
`                ``}`
`                ``it.remove();`
`            ``}        `
`            ``max = Math.max(max, level);`
`        ``}`

`        ``return` `max;`
`    ``}`
HashMap<String, Integer> found = new HashMap<>();-google 1point3acres
public int getLongestChainHelper(Set<String> dict, String word) {
if(dict == null){
return 0;
}
if(word.length() == 1){
return 1;
}
if(found.containsKey(word)) {
return found.get(word);
}
int maxLen = -1;
for(int i = 0; i < word.length(); i++) {
String newWord = removeCharAt(word, i);
if(dict.contains(newWord)) {
int longestChainNewWord = getLongestChainHelper(dict, newWord);. 涓€浜�-涓夊垎-鍦帮紝鐙鍙戝竷
if(longestChainNewWord + 1 > maxLen) {-google 1point3acres
maxLen = longestChainNewWord + 1;
}
}
}
maxLen = maxLen == -1 ? 1 : maxLen;
found.put(word, maxLen);
return maxLen;
}. 鐣欏鐢宠璁哄潧-涓€浜╀笁鍒嗗湴

public int getLongestChain(Set<String> dict) {
if(dict == null){
return 0;. Waral 鍗氬鏈夋洿澶氭枃绔�,
}
int longestChain = -1;
int currLongest = getLongestChainHelper(dict, word);
if( currLongest > longestChain) {
longestChain = currLongest;
}
}
return longestChain;.1point3acres缃�
}