【NOIP2015】子串

本题在洛谷上的链接:https://www.luogu.org/problemnew/show/P2679


好难的DP题,哎,今年要是遇到这样的DP题就放弃得了。。。

本来打算像那道统计单词个数一样,先对字符串进行预处理,然后跑DP,想了个思路,复杂度很高,而且代码也不好实现。

看大佬的博客里,用的方法类似LIS。就是讨论a中第i个字符取不取。

定义状态dp[i][j][k][s],s=0表示所有的方案数,s=1表示必定取第i个字符的方案数。必须满足a[i]=b[j]才可以取第i个字符,此时dp[i][j][k][1]=dp[i-1][j-1][k-1][0]+dp[i-1][j-1][k][1],否则dp[i][j][k][1]=0;

dp[i][j][k][0]自然就是dp[i-1][j][k][0]+dp[i][j][k][1],仔细想想的确是对的,但要自己想出来好难啊!

虽然时间不会超,但空间会炸,需要滚动数组,因为我们发现第1维只用到了i和i-1,所以写成pre和now就好。

再就是初始化dp[pre][0][0][0]=dp[now][0][0][0]=1,统计方案数的DP初始化最坑了,一般是把一种啥也不干的dp值设为1。

最后别忘了对1e9+7取模。

 1 #include <cstdio>
 2 
 3 const int maxn = 1005, maxm = 205, maxk = 205, P = 1e9 + 7;
 4 
 5 int dp[2][maxm][maxk][2];
 6 
 7 char a[maxn], b[maxm];
 8 
 9 inline void swap(int &a, int &b) {
10     int t = a; a = b, b = t;
11 }
12 
13 int main() {
14     int n, m, k, pre = 0, now = 1;
15     scanf("%d%d%d%s%s", &n, &m, &k, a + 1, b + 1);
16     dp[pre][0][0][0] = dp[now][0][0][0] = 1;
17     for (int i = 1; i <= n; ++i, swap(pre, now))
18         for (int j = 1; j <= m; ++j)
19             for (int p = 1; p <= k; ++p) {
20                 if (a[i] == b[j])
21                     dp[now][j][p][1] = (dp[pre][j - 1][p - 1][0] + dp[pre][j - 1][p][1]) % P;
22                 else dp[now][j][p][1] = 0;
23                 dp[now][j][p][0] = (dp[pre][j][p][0] + dp[now][j][p][1]) % P;
24             }
25     printf("%d", dp[pre][m][k][0]);
26     return 0;
27 }
AC代码
原文地址:https://www.cnblogs.com/Mr94Kevin/p/9815345.html