本题出自力扣题库第1638题。题面大意如下:
给定两个字符串s和t,找出s中的非空子串的数目,这些子串满足替换 一个不同字符以后,是t的子串。 换言之,请你找到s和t串中恰好只有一个字符不同的子字符串对的数目。
示例:
输入:s = "aba", t = "baba"
输出:6
s中的第一个a,t中的第一个b
s中的第一个a,t中的第二个b
s中的第二个a,t中的第一个b
s中的第二个a,t中的第二个b
s中的b,t中的第一个a
s中的b,t中的第二个a
题解:
本题是双字符串输入类的DP应用, 根据题意,我们可以得到如下的DP表达式
F(i,j)
= F(i - 1, j - 1), s[i]和t[j]相同
= 1 + 从s[i]和t[j]往前倒数,相同的字符的个数, s[i]和t[j]不同
这里i和j分别时s与t的字符串数组下标,F(i,j)返回的是s中前i个字符的字串与t中前j个字符的字串之间符合题目要求的子串的数量。从题面出发,当s[i]和t[j]两个字符相同时,F(i,j)的返回值就是F(i - 1, j - 1),也就是说s中前i-1个字符的字串与t中前j-1个字符的字串之间符合题目要求的子串的数量;而当s[i]和t[j]两个字符不同时,我们就要从s[i]和t[j]往前倒数,看有多少个相同的字符,加上1后,就是有多少个符合题目要求的子串的数量。
从以上表达式出发,我们定义一个二维数组,并使用双层循环依据s和t的字符串数组下标从小到大依次计算DP数组中各个元素的值,循环完成后,对应s和t最后一个字符位置的DP数组元素中的数值就是问题的答案。
Java代码如下:
class Solution {
public int countSubstrings(String s, String t) {
char[] sChars = s.toCharArray();
char[] tChars = t.toCharArray();
int sLength = sChars.length;
int tLength = tChars.length;
int dp[][] = new int[sLength + 1][tLength + 1];
int count = 0;
for (int i = 1; i <= sLength; i ++) {
for (int j = 1; j <= tLength; j ++) {
if (sChars[i - 1] == tChars[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = 1;
int m = i - 1, n = j -1;
while (m >= 1 && n >= 1) {
if (sChars[m - 1] == tChars[n - 1]) {
dp[i][j] ++;
m --;
n --;
} else {
break;
}
}
}
count += dp[i][j];
}
}
return count;
}
}
以上算法的进一步优化是使用另一个DP数组来减少s[i]和t[j]不同时的计算次数。