题目链接:LibreOJ 2424 子串
题目大意:
题解:
设(dp[j][k])表示字符串(A)判断到第(i)个字符且一定取第(i)个字符,组成字符串(B)的前(j)个字符,用了(k)个子串的方案数。
设(DP[j][k])表示字符串(A)判断到第(i)个字符但不一定取第(i)个字符,组成字符串(B)的前(j)个字符,用了(k)个子串的方案数。
-
若(A[i-1] eq B[j-1]),则(dp[j][k] = 0);
-
若(A[i-1]=B[j-1]),可以选择将(A[i-1])加在(A[i-2])后面算作一个子串,则此时(A[i-2])必须可取且要一定取,方案数为(dp[j-1][k]);若将(A[i-1])算作一个新的子串,则(A[i-2])不一定取,方案数为(DP[j-1][k-1]),则状态转移方程为:
[left{egin{aligned}dp[j][k] &= dp[j - 1][k] + DP[j - 1][k - 1],&A[i-1]= B[j-1]\dp[j][k] &= 0,&A[i-1]
eq B[j-1]end{aligned}
ight.
]
[DP[j][k] = sum_{1}^{i} dp[j][k]
]
看到这肯定发现(i)所在的那一维被压缩了,所以在(j)的循环里,(j)是从(m)到(1)递减更新答案的(可以联想(01)背包压缩成一维数组)。
#include <iostream>
#include <string>
using namespace std;
#define N 1010
const int mod = 1e9 + 7;
int dp[210][210], DP[210][210], n, m, t;
string a, b;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> t;
cin >> a >> b;
dp[0][0] = DP[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = m; j >= 1; --j) {
for (int k = 1; k <= t; ++k) {
if (a[i - 1] == b[j - 1]) {
dp[j][k] = (dp[j - 1][k] + DP[j - 1][k - 1]) % mod;
DP[j][k] = (DP[j][k] + dp[j][k]) % mod;
} else {
dp[j][k] = 0;
}
}
}
}
cout << DP[m][t];
return 0;
}