本题出自力扣题库第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];
}
}