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
https://www.cnblogs.com/EdwardLiu/p/6177843.html
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
另外一种解法:
思路: 建一个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 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
http://www.1point3acres.com/bbs/thread-131978-1-1.html
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;
for( String word : dict) {. more info on 1point3acres.com
int currLongest = getLongestChainHelper(dict, word);
if( currLongest > longestChain) {
longestChain = currLongest;
}
}
return longestChain;.1point3acres缃�
}
https://blog.cancobanoglu.net/2016/09/18/interview-questions-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
https://www.cnblogs.com/EdwardLiu/p/6177843.html
DP use HashMap:
根据string的长度sort,然后维护每个string的longest chain,default为1,如果删除某个char生成的string能提供更长的chain,则更新
9 public int findLongestChain(String[] words) { 10 if (words==null || words.length==0) return 0; 11 int longestChain = 0; 12 Arrays.sort(words, new Comparator<String>() { 13 public int compare(String str1, String str2) { 14 return str1.length()-str2.length(); 15 } 16 }); 17 HashMap<String, Integer> map = new HashMap<String, Integer>(); 18 for (String word : words) { 19 if (map.containsKey(word)) continue; 20 map.put(word, 1); 21 for (int i=0; i<word.length(); i++) { 22 StringBuilder sb = new StringBuilder(word); 23 sb.deleteCharAt(i); 24 String after = sb.toString(); 25 if (map.containsKey(after) && map.get(after)+1 > map.get(word)) { 26 map.put(word, map.get(after)+1); 27 } 28 } 29 if (map.get(word) > longestChain) longestChain = map.get(word); 30 } 31 return longestChain; 32 }
X. If we don't sort, then we have to dfs
static int longest_chain(String[] w) {
if (null == w || w.length < 1) {
return 0;
}
int maxChainLen = 0;
HashSet<String> words = new HashSet<>(Arrays.asList(w));
HashMap<String, Integer> wordToLongestChain = new HashMap<>();
for (String word : w) {
if (maxChainLen > word.length()) {
continue;
}
int curChainLen = find_chain_len(word, words, wordToLongestChain) + 1;
wordToLongestChain.put(word, curChainLen);
maxChainLen = Math.max(maxChainLen, curChainLen);
}
return maxChainLen;
}
static int find_chain_len(String word, HashSet<String> words,
HashMap<String, Integer> wordToLongestChain) {
int curChainLen = 0;
for (int i = 0; i < word.length(); i++) {
String nextWord = word.substring(0, i) + word.substring(i + 1);
if (words.contains(nextWord)) {
if (wordToLongestChain.containsKey(nextWord)) {
curChainLen = Math.max(curChainLen, wordToLongestChain.get(nextWord));
} else {
int nextWordChainLen = find_chain_len(nextWord, words, wordToLongestChain);
curChainLen = Math.max(curChainLen, nextWordChainLen + 1);
}
}
}
return curChainLen;
}
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+
"----"
);
}
}
};
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
另外一种解法:
思路: 建一个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 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
思路不错,不过有bug,你从maxLen开始算,但如果longestChain不是从maxLen的String开始的,你的code就得不到正确结果,应该loop一下所有的len,当然,如果len已经比longestChain短了,就可以结束了
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;
for( String word : dict) {. more info on 1point3acres.com
int currLongest = getLongestChainHelper(dict, word);
if( currLongest > longestChain) {
longestChain = currLongest;
}
}
return longestChain;.1point3acres缃�
}
https://blog.cancobanoglu.net/2016/09/18/interview-questions-string-chain/