在问 · 算法题解

1048. 最长字符串链

题目描述

输入单词数组 words,每个单词由小写字母组成。

如果在单词 A 中某个位置插入恰好一个字母,并且不改动其他字母的顺序,就能得到单词 B,则称 A 是 B 的前身。例如 abc 是 abac 的前身,而 cba 不是 bcad 的前身。

单词链是一个序列 [w1, w2, ..., wk](k >= 1),其中每个单词都是下一个单词的前身。单个单词本身也是长度为 1 的链。

从 words 中挑选单词组成单词链,返回链的最大可能长度。

示例 1
输入:words = ["a","b","ba","bca","bda","bdca"]
输出:4

解释:例如 a -> ba -> bda -> bdca,长度 4。

示例 2
输入:words = ["xbc","pcxbcf","xb","cxbc","pcxbc"]
输出:5

解释:五个单词能串成 xb -> xbc -> cxbc -> pcxbc -> pcxbcf。

示例 3
输入:words = ["abcd","dbqca"]
输出:1

解释:dbqca 中字母顺序与 abcd 不一致,无法相连,只能各自成链。

数据范围

  • 1 <= words.length <= 1000
  • 1 <= words[i].length <= 16
  • words[i] 只含小写英文字母。

解题思路

前身一定比后继短 1,所以按长度从短到长处理。设 dp[w] 为以 w 结尾的最长链长度。与其去找“谁能插一个字母变成 w”,不如反过来:依次删掉 w 的第 k 个字母得到 prev,如果 prev 在 dp 里,就能接上:dp[w] = max(dp[prev] + 1);一个都没有则为 1。答案是所有 dp 的最大值。

为什么正确

处理 w 时,所有更短的单词都已在 dp 中且已是最优,而 w 的前身恰好都是删一个字母得到的字符串。

复杂度分析

时间 O(n log n + n·L²)(L 为单词长度,删字母生成字符串 O(L)),空间 O(n·L)。

易错点

必须先按长度排序,否则算 w 时它的前身可能还没算。不要按字典序排。前身可以在任意位置插字母,不只是末尾。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def longestStrChain(self, words: List[str]) -> int:
        words.sort(key=len)
        dp = {}
        ans = 0
        for w in words:
            best = 1
            for k in range(len(w)):
                prev = w[:k] + w[k + 1:]
                if prev in dp:
                    best = max(best, dp[prev] + 1)
            dp[w] = best
            ans = max(ans, best)
        return ans

载入编辑器,自己跑一遍 →

动态规划 专题的其他题目

查看 动态规划 全部题目 →