给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
题目数据保证答案符合 32 位带符号整数范围。
简单二维DP问题。设dp[i, j]表示以i结尾的子串含有t的长度为j的前缀的数量,二重循环遍历:
(1)s[i] == t[j]: 转移方程为(dp[i][j] += dp[i - 1][j - 1] + dp[i - 1][j]),含义是当前可以由两种情况转移过来:
(a) s[i]恰好就是t[j],s中以i - 1为结尾的子串包括t的长度为j - 1的前缀的情况数。
(b)s中以i - 1为结尾的子串已经包括整个t的长度为j的前缀的情况数。
(2)s[i] != t[j]: 转移方程为(dp[i][j] += dp[i - 1][j]),含义同上。
初始化需要处理所有的dp[i, 1]为当前已经出现过的t[1]的数量。(代码中其实没必要开cnt数组存储)
注意j初始化完了以后要从2开始,同时要开long long(虽然答案说范围在int32范围内)。还有一个坑点就是输入的字符串是大小写混合的。
#include <iostream>
using namespace std;
long long dp[1005][1005];//dp[i][j]表示以i结尾的子串含有t的长度为j的前缀的数量
long long cnt[100];
int lens, lent;
int main() {
memset(cnt, 0, sizeof(cnt));
memset(dp, 0, sizeof(dp));
string s, t;
cin >> s >> t;
lens = s.size(), lent = t.size();
s = " " + s, t = " " + t;
for(int i = 1; i <= lens; i++) {
cnt[s[i] - 'A']++;
dp[i][1] += cnt[t[1] - 'A'];
}
for(int i = 1;i <= lens; i++) {
for(int j = 2; j <= lent; j++) {//注意j初始化完了以后要从2开始
if(s[i] == t[j]) {
dp[i][j] += dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] += dp[i - 1][j];
}
}
}
cout << dp[lens][lent];
return 0;
}