Forever Young

洛谷 P2679 子串

思路

这个题主要的难度在 \(DP\) 的状态设计上,如果 \(DP\) 状态设计好了,转移也就不难想了。

\(f[i][j][k][0/1]\) 表示A串中前 \(i\) 个字符,用了 \(k\) 个子串,匹配了B串前 \(j\) 个字符,用没用当前第 \(i\) 个字符的方案数,那么有

\[\begin{cases}f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1]\\f[i][j][k][1]=f[i-1][j-1][k-1][1]+f[i-1][j-1][k-1][0]+f[i-1][j-1][k][1],A[i]=B[j]\end{cases} \]

考虑初始条件

  • 如果 \(A[i]=B[1]\),那么有 \(f[i][1][1][1]=1\)
  • b不难发现,\(f[i][1][1][0]\) 的值为 \(\sum\limits_{j=1}^{i-1}[A[j]=B[1]]\),表示不选第 \(i\) 个字符,从前 \(i\) 个字符中取出 \(1\) 个子串匹配B串第 \(1\) 个字符的方案数为 \(A[1\sim i-1]\) 中与 \(B[1]\) 相同的字符的个数。

最后的答案就是 \(f[n][m][k][0]+f[n][m][k][1]\)

至此 \(DP\) 就很明了了,但是空间是开不下的,但是发现 \(i\) 的转移只与 \(i-1\) 有关,所以可以用滚动数组去掉一维,这样就能过了。

时间复杂度为 \(O(nmk)\)

代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 1e3 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
  char c = getchar();
  int x = 0, f = 1;
  for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
  for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
  return x * f;
}

char a[A], b[A];
int f[2][201][201][2];
int n, m, t, now = 0, las = 1, cnt;

int main() {
  n = read(), m = read(), t = read();
  scanf("%s%s", a + 1, b + 1);
  for (int i = 1; i <= n; i++) {
    swap(now, las);
    f[now][1][1][0] = cnt;
    if (a[i] == b[1]) f[now][1][1][1] = 1, cnt++;
    for (int j = 2; j <= m; j++) {
      for (int k = 1; k <= t; k++) {
        if (a[i] == b[j]) 
          f[now][j][k][1] = ((f[las][j - 1][k - 1][1] + f[las][j - 1][k - 1][0]) % mod + f[las][j - 1][k][1]) % mod;
        f[now][j][k][0] = (f[las][j][k][0] + f[las][j][k][1]) % mod;
      }
    }
    memset(f[las], 0, sizeof(f[las]));
  }
  cout << (f[now][m][t][1] + f[now][m][t][0]) % mod;
  return 0;
} 
posted @ 2020-09-17 10:03  Loceaner  阅读(150)  评论(2编辑  收藏  举报