Sunday, May 15, 2016

LIKE CODING: MJ [56] Longest String Chain

LIKE CODING: MJ [56] Longest String ChainGiven 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 stringFor 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] = newTrieNode(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] = newTrieNode(w);

extend(hash[w]);

}

}

}

int getMaxLen(vector<string> dict){

build(dict);

int ret = 0;

for(auto w:dict){

ret = max(ret, getMaxLen(hash[w]));

}

returnret;

}

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;

}

}

returnnode->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 equalto res, no need to try BFS on it, because from this word we cannot find a longer length. Or when thelength 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. Becausein this problem the length of a child word must be shorted than its parent, so a child will neverpoint reversely to its parent, thus we don't need to worry about a cycle. So this problem is excatelya 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 arrayUse 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
另外一种解法：思路: 建一个map<长度，set<string>> 根据长度把输入的字典放到set里，然后从最长的长度所在的map[长度] 开始枚举每个单词并缩减然后进行recursive call， 比如bdca 那么就可能缩成dca,bca,bda 然后去map[3]里找，类推直到 word.size()==1 或找不到， 放个全局int去记最大长度。注意找到后从字典里删除和word ladder一样 否则只能过4个case 会超时。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)dict.Add(s);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 sif(s.Length<=res || s.Length==1)continue;res = Math.Max(res,Bfs(s, dict));}return res;}//Find the length of longest chain starting from sprivate 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 1q.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