算是一个计数类的\(dp\),首先我们先确定状态数有多少个,一个是字符串\(a\)现在匹配到了第几位,一个是字符串\(b\)现在匹配到了第几位,另一个就是我们现在分成了多少组从字符串\(a\)中挑选出一段子串。这三个状态是最直接的状态,还有另一个就是我们是否要选择当前的字符\(a[i]\)来进行匹配,这个只有两种可能也就是选或不选两种,我们分别用\(0\)和\(1\)来表示选或不选的决策。
对于我们状态转移的过程中只会出现两种可能也就是\(a[i] == b[j]\)或者\(a[i] \neq b[j]\)这两种可能。
我们可以选择选当前的位置上的\(a[i]\)去匹配,也可以不选择,这就又是两种决策了,所以这个状态我们在转移的时候选或不选两种都是要更新的。
如果是选就是
dp[i][j][p][1] = dp[i - 1][j - 1][p][1] + dp[i - 1][j - 1][p - 1][1] + dp[i - 1][j - 1][p - 1][0];
其中dp[i][j][p][1]
表示的是在我们选择用当前的\(a[i]\)去和\(b[j]\)匹配并且将\(a[i]\)放在当前的子串中,dp[i - 1][j - 1][p][1]
表示的是使用匹配前一次的状态并且使用当上一个字符的方案数,dp[i - 1][j - 1][p - 1][1]
表示的是将当前匹配上的字符纳入上一次的子串中,并且使用上一次的字符的方案数,dp[i - 1][j - 1][p - 1][0]
表示的就是使用匹配前一次的状态并且不使用上一次匹配的字符的方案数。
如果不选择当前的字符,那就是
dp[i][j][p][0] = dp[i - 1][j][p][0] + dp[i - 1][j][p][1];
表示的就是没有使用字符,并且\(b\)串匹配的长度还是\(j\)的方案数。
我们只能选择不匹配的情况,转移方程同上。还有就是因为匹配不上所以这一次如果是选的这个字符的话,那么方案数一定是0,所以还有一个赋值的过程
dp[i][j][p][0] = dp[i - 1][j][p][0] + dp[i - 1][j][p][1]; dp[i][j][p][1] = 0;
完成上面的操作这个题就完成了
int main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n, m, k; std::cin >> n >> m >> k; std::string sa, sb; std::cin >> sa >> sb; sa = " " + sa; sb = " " + sb; dp[1][0][0][0] = 1; dp[0][0][0][0] = 1; for (int i = 1; i <= n; i ++ ) { for (int j = 1; j <= m; j ++ ) { for (int p = 1; p <= k; p ++ ) { if (sa[i] == sb[j]) { dp[i % 2][j][p][1] = dp[(i - 1) % 2][j - 1][p][1] + dp[(i - 1) % 2][j - 1][p - 1][1] + dp[(i - 1) % 2][j - 1][p - 1][0]; dp[i % 2][j][p][0] = dp[(i - 1) % 2][j][p][0] + dp[(i - 1) % 2][j][p][1]; } else { dp[i % 2][j][p][0] = dp[(i - 1) % 2][j][p][0] + dp[(i - 1) % 2][j][p][1]; dp[i % 2][j][p][1] = 0; } } } } Z ans = dp[n % 2][m][k][1] +dp[n % 2][m][k][0]; std::cout << ans.val() << "\n"; return 0; }