返回

洞察 LeetCode:化繁为简,攻克「792. 匹配子序列的单词数」

见解分享

算法竞赛:LeetCode 792 题解——匹配子序列的单词数

前言

在算法竞赛领域,LeetCode 作为领军者,汇聚了众多引人入胜的难题,激发着无数开发者的求知欲和挑战精神。今天,让我们深入剖析 LeetCode 的一道中等难度题目——「792. 匹配子序列的单词数」。

问题概述

「匹配子序列的单词数」要求我们找出给定单词数组 words 中有多少个单词是源字符串 s 的子序列。子序列是指从原始字符串中删除任意数量字符(包括不删除任何字符)而形成的新字符串。

例如,字符串 "abc" 是 "abac" 的子序列,"ab" 也是 "abac" 的子序列。

直观解法

最直接的解法是逐个比较 swords 中的每个单词。对于每个单词 word,我们可以遍历 s,检查 word 中的每个字符是否按顺序出现在 s 中。如果所有字符都存在,则 words 的子序列,我们增加计数器。

虽然直观易懂,但这种方法的时间复杂度为 O(s * n * m),其中 ss 的长度,nwords 中单词的数量,mword 的平均长度。对于海量数据,这种方法的效率会非常低下。

动态规划解法

为了提高效率,我们可以采用动态规划的方法。我们定义一个二维数组 dp,其中 dp[i][j] 表示 s 的前 i 个字符和 word 的前 j 个字符的匹配情况。

初始化 dp 数组:

dp[0][0] = 1  # 空串与空串匹配

递推公式:

dp[i][j] = dp[i - 1][j]  # s[i] 与 word[j] 不匹配,跳过 s[i]
if s[i] == word[j]:
    dp[i][j] += dp[i - 1][j - 1]  # s[i] 与 word[j] 匹配

最终结果为 dp[s.length][word.length]

代码实现

def numMatchingSubseq(s, words):
    dp = [[0] * (len(word) + 1) for word in words]

    for i in range(len(s)):
        for j, word in enumerate(words):
            if not word:
                dp[j][0] = 1
            elif s[i] == word[0]:
                dp[j][1] += dp[j][0]
            else:
                dp[j][1] = dp[j][0]

            for k in range(1, len(word)):
                if s[i] == word[k]:
                    dp[j][k + 1] += dp[j][k]

    return [dp[i][-1] for i in range(len(words))]

复杂度分析

动态规划解法的时间复杂度仍为 O(s * n * m),但由于避免了不必要的比较,因此常数项更小,效率更高。

总结

通过解决这道 LeetCode 难题,我们深入理解了子序列的概念,掌握了动态规划这一重要的算法思想。面对算法问题,我们需要灵活运用合适的解法,不断提升自己的算法技能和编程素养。愿每一位算法爱好者都能在 LeetCode 的海洋中乘风破浪,所向披靡!

常见问题解答

1. 如何判断一个单词是否是子序列?

一个单词是子序列,当且仅当它可以从另一个字符串中删除任意数量的字符(包括不删除任何字符)而得到。

2. 动态规划解法是如何工作的?

动态规划解法通过构造二维数组 dp,其中 dp[i][j] 表示 s 的前 i 个字符和 word 的前 j 个字符的匹配情况,从而避免不必要的比较,提高效率。

3. 动态规划解法的递推公式是如何推导的?

递推公式 dp[i][j] 的推导基于这样的思考:如果 s[i]word[j] 匹配,那么 dp[i][j] 等于 dp[i - 1][j - 1]s[i] 匹配 word[j])加上 dp[i - 1][j]s[i] 不匹配 word[j]);如果 s[i] 不与 word[j] 匹配,那么 dp[i][j] 等于 dp[i - 1][j]s[i] 跳过)。

4. 直观解法和动态规划解法的主要区别是什么?

直观解法逐个比较 swords 中的每个单词,而动态规划解法通过构造 dp 数组,避免不必要的比较,提高效率。

5. 这道题目在算法竞赛中应用广泛吗?

是的,这道题目作为一道中等难度的 LeetCode 题,经常出现在算法竞赛中,考察选手的算法思维和动态规划技巧。