【线性 dp】B014_LC_最长字符串链(暴力 / map 优化)

一、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

  • 定义状态
    • f [ i ] f[i] f[i] 表示 ws 的前i个单词形成的最长词链长度
  • 思考初始化:
    • f [ 0... n ] = 0 f[0...n] = 0 f[0...n]=0
  • 思考状态转移方程
    • 如果 w s [ i ] ws[i] ws[i] w s [ j ] ws[j] ws[j] ( i < n   & &   i < j ) (ii<n && i<j 的前身,则有 f [ j ] = f [ i ] + 1 f[j] = f[i] + 1 f[j]=f[i]+1
  • 思考输出 m a x ( f [ 0... n ] ) + 1 max(f[0...n]) + 1 max(f[0...n])+1
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;
    }
}

复杂度分析

  • 时间复杂度: O ( n 2 × ∑ 0 n − 1 w s [ i ] . l e n g t h ) O(n^2 × \sum_{0}^{n-1}ws[i].length) O(n2×0n1ws[i].length)
  • 空间复杂度: O ( n ) O(n) O(n)

方法二: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;
    }
}

复杂度分析

  • 时间复杂度: O ( n × ∑ 0 n − 1 w s [ i ] . l e n g t h ) O(n × \sum_{0}^{n-1} ws[i].length) O(n×0n1ws[i].length)
  • 空间复杂度: O ( n ) O(n) O(n)

你可能感兴趣的:(#,线性,dp)