【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 }