Dynamic Programming学习笔记 (30) - 判断子序列 (力扣# 392)

130 阅读2分钟

本题出自力扣题库第392题。题面大意如下:

给定字符串s和t,判断s是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。

实例:

s = "ace", t= "abcde"
输出:true

s = "aec", t= "abcde"
输出:false

对于这个题面而言,DP并不一定是最佳的选择,但可以用来作为DP入门的练习。

根据双字符串输入,我们可以定义如下DP表达式:

F(i,j)
  = F(i + 1, j + 1), s[i] == t[j]
  = F(i, j + 1), s[i] != t[j]
  = true, (i >= s.length)

其中,i和j分别是s和t的字符串数组下标,F(i,j)返回的是s从i开始的子串是否是t从j开始的子串的子序列。对于给定的i和j来说,有两种基本情况,当s[i]和t[j]相同时,F(i,j)返回F(i + 1, j + 1),表示对于s和t来说,都从下一个字符继续比较;当s[i]和t[j]不同时,F(i,j)返回F(i, j + 1),表示s的当前字符不变,而t则需要继续到下一个字符。作为边界情况,当i超过s的长度后,F(i,j)返回true,因为此时从i开始的字串为空,是t的所有子串的子序列。

从以上表达式出发,我们使用一个二维DP数组,并使用双重循环,外层是s的字符串数组下标,内层是t的字符串数组下标,两者都是从大到小依次计算DP数组中各个元素的值,最后, DP[0][0]中的数值就是问题的最终答案。

Java代码如下:

class Solution {
    public boolean isSubsequence(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();

        if (sLen > tLen) {
            return false;
        }

        char[] sChars = s.toCharArray();
        char[] tChars = t.toCharArray();

        boolean[][] dp = new boolean[sLen + 1][tLen + 1];
        for (int j = 0; j <= tLen; j ++) {
            dp[sLen][j] = true;
        }

        for (int i = sLen - 1; i >=0; i--) {
            for (int j = tLen - 1; j >=0; j--) {
                if (sChars[i] == tChars[j]) {
                    dp[i][j] = dp[i + 1][j + 1];
                } else {
                    dp[i][j] = dp[i][j + 1];
                }
            }
        }

        return dp[0][0];
    }
}