返回

LeetCode 392:判断子序列,解锁隐藏宝藏

后端

用动态规划征服「判断子序列」:算法寻宝之旅

在算法竞赛中,字符串匹配是一个不可忽视的挑战。本文将带你踏上破解 LeetCode 392「判断子序列」的算法寻宝之旅,助你征服字符串匹配的险关。

子序列的奥秘

子序列是指一个字符串在不改变原有字符顺序的情况下,删除任意数量(包括 0 个)字符后形成的新字符串。例如,"ace" 是 "abcde" 的一个子序列,因为你可以移除 "b" 和 "d" 而不影响 "a"、"c" 和 "e" 的相对位置。

理解题目要求

LeetCode 392「判断子序列」要求判断字符串 s 是否为 t 的子序列。换句话说,你需要确定是否可以在 t 中找到一个子序列与 s 完全匹配。

算法策略:动态规划

解决这道题目的关键是动态规划,一种解决复杂问题的大杀器。它将大问题分解成一系列较小的子问题,并逐步解决这些子问题,最终得出整体解。

动态规划转移方程

在本例中,我们可以创建一个二维表格 dp,其中 dp[i][j] 表示字符串 s 的前 i 个字符是否可以匹配字符串 t 的前 j 个字符的子序列。

dp[i][j] = 
{
    dp[i-1][j-1]  if s[i] = t[j]
    dp[i][j-1]    if s[i] != t[j]
}

边界条件

  • dp[0][j] = true,表示空字符串可以匹配任何字符串的前 j 个字符。
  • dp[i][0] = false,表示空字符串不能匹配非空字符串的前 0 个字符。

代码实现

public class Solution {
    public boolean isSubsequence(String s, String t) {
        int m = s.length(), n = t.length();
        boolean[][] dp = new boolean[m + 1][n + 1];
        for (int i = 0; i <= m; i++) {
            dp[i][0] = true;
        }
        for (int j = 1; j <= n; j++) {
            dp[0][j] = false;
        }
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s.charAt(i - 1) == t.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = dp[i][j - 1];
                }
            }
        }
        return dp[m][n];
    }
}

总结

通过本文,你已经掌握了「判断子序列」的算法策略和代码实现。动态规划的思想将助你轻松破解字符串匹配的难题。在算法竞赛的征途中,算法和数据结构是不可或缺的利器。持续练习,磨炼你的技能,成为一名算法大师吧!

常见问题解答

  1. 如何判定空字符串是否为子序列?
    空字符串可以匹配任何字符串的前 j 个字符。

  2. 为什么动态规划的边界条件不同?
    因为空字符串可以匹配任何字符串,而空字符串不能匹配非空字符串。

  3. dp[i][j] 表示什么?
    dp[i][j] 表示字符串 s 的前 i 个字符是否可以匹配字符串 t 的前 j 个字符的子序列。

  4. 如何判断 s 是 t 的子序列?
    当 dp[m][n] 为 true 时,表明 s 是 t 的子序列。

  5. 动态规划的思想是什么?
    动态规划将大问题分解成一系列较小的子问题,并逐步解决这些子问题,最终得出整体解。