给出一个单词列表,其中每个单词都由小写英文字母组成。如果我们可以在 word1 的任何地方添加一个字母使其变成 word2,那么我们认为 word1 是 word2 的前身。例如,“abc” 是 “abac” 的前身。词链是单词 [word_1, word_2, …, word_k] 组成的序列,k >= 1,其中 word_1 是 word_2的前身,word_2 是 word_3 的前身,依此类推。从给定单词列表 words 中选择单词组成词链,返回词链的最长可能长度。
示例:
输入:[“a”,“b”,“ba”,“bca”,“bda”,“bdca”]
输出:4
解释:最长单词链之一为"a",“ba”,“bda”,“bdca”。提示:
1 <= words.length <= 1000
1 <= words[i].length <= 16
words[i]仅由小写英文字母组成。
思路:典型的动态规划
- 先把words[]中的字符串从短到长排序
- 定义dp[],长度为words.legnth(),其中dp[i]表示words[0…i] (从下标0到i)中的最长字符串链。重点:dp[i] = max{ dp[j] }+1,其中,j是words[i]的前身下标,j属于{0,…,i-1}。如果words[0…i-1]中不存在words[i]的前身,那么dp[i] = 1。
- 返回dp[]中的最大值
代码:
//最長字符串鏈
public int longestStrChain(String[] words) {
//對words按字符長度 排序
Arrays.sort(words, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
int[] dp = new int[words.length];
for(int i=0;i<dp.length;++i){
int tail = i-1;
int max = Integer.MIN_VALUE;
while(tail>=0){
if(IsChild(words[tail],words[i])){
if(dp[tail]>max){
max = dp[tail];
}
}
tail--;
}
dp[i] = max==Integer.MIN_VALUE ? 1:max+1;
}
Arrays.sort(dp);
return dp[dp.length-1];
}
private boolean IsChild(String child, String father) {
if(child.length()+1!=father.length())
return false;
else{
char[] ch = child.toCharArray();
char[] fa = father.toCharArray();
int pos = fa.length-1; //找到第一個不同的位置
for(int i=0;i<ch.length;++i){
if(fa[i]!=ch[i]){
pos = i;
break;
}
}
//將fa[pos]插入ch[i]上
char[] res = new char[fa.length];
for(int i=0;i<res.length;++i){
if(i==pos){
res[i] = fa[i];
}
else if(i<pos){
res[i] = ch[i];
}else{
int j = i-1;
res[i] = ch[j];
}
}
String s = String.valueOf(res);
return s.equals(father);
}
}