一、Problem
給出一個單詞列表,其中每個單詞都由小寫英文字母組成。
如果我們可以在 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] 僅由小寫英文字母組成。
二、Solution
方法一:dp
- 定義狀態:
- 表示 ws 的前i個單詞形成的最長詞鏈長度
- 思考初始化:
- 思考狀態轉移方程:
- 如果 是 的前身,則有
- 思考輸出:
class Solution {
boolean ck(String a, String b) {
int n = a.length(), m = b.length(), i = 0, j = 0;
if (n+1 != m)
return false;
while (i < n && j < m) {
if (a.charAt(i) == b.charAt(j))
i++;
j++;
}
return i == n;
}
public int longestStrChain(String[] ws) {
Arrays.sort(ws, (s1, s2) -> s1.length() - s2.length());
int n = ws.length, max = 0, f[] = new int[n+1];
for (int i = 0; i < n-1; i++)
for (int j = i+1; j < n; j++) {
if (ck(ws[i], ws[j])) {
f[j] = f[i] + 1;
max = Math.max(max, f[j]);
}
}
return max + 1;
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,
方法二:map 優化
在方法一中,我們用了兩重循環枚舉一個單詞是否是另一個單詞前身,其實我們只需要枚舉一個字符串的子串(之比源串的字符少 1 的子串)是否出現出現過即可,如果出現過,證明該源串是詞鏈的一部分
class Solution {
public int longestStrChain(String[] ws) {
Arrays.sort(ws, (s1, s2) -> s1.length() - s2.length());
int n = ws.length, ans = 0;
Map<String, Integer> mp = new HashMap<>();
for (int i = 0; i < n; i++) {
int cur = 0;
for (int j = 0; j < ws[i].length(); j++) {
String t = ws[i].substring(0, j) + ws[i].substring(j+1, ws[i].length());
int c = mp.getOrDefault(t, 0) + 1;
cur = Math.max(cur, c);
}
if (cur > ans)
ans = cur;
mp.put(ws[i], cur);
}
return ans;
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,