P2679 子串
题面
题解
noip快到了,所以来刷点历年真题。
这道题经典的字符串匹配模型。
我们还是按照套路设 \(f[i][j][k]\) 表示 从A串的前 \(i\) 位中,取出 \(k\) 个字符串,且B串匹配到第 \(j\) 位的方案数。
可是你会发现你这样有可能会少算,因为与B串第 \(j\) 位相同的字母,他可以放也可以不放,这是两种情况。
因此,我们在添加一维 \(f[i][j][k][0/1]\) 表示 从A串的前 \(i\) 位中,取出 \(k\) 个字符串,且B串匹配到第 \(j\) 位,且A串第 \(i\) 个字符在第 \(k\) 个字符串的的方案数
初始化 \(f[i][0][0] = 1\) 因为从他这才开始与B串开始匹配,也是一种情况。
转移:
- a[i] != b[j] 这时候A串第 \(i\) 位,我们是不可以选他的,因为他与B串不匹配了,即使我们后面再怎么算,也不会使 A串与B串相等
f[i][j][k][0] = (f[i-1][j][k][0] + f[i-1][j][k][1])%p;//他的前一位可以选也可以不选
f[i][j][k][1] = 0;//他这一位的字符一定不会被选,因为我们选了这个字符,后面再怎么选,也不会与B串相等
-
a[i] == b[j] 他这一位与 B串第 \(j\) 位相等,但我们可以选他也可以不选。
不选的情况就和不相等的情况一样,相等的话讨论是与前面的字符共同在一个串还是自己在新开一个串就行了。
f[i][j][k][0] = (f[i-1][j][k][0] + f[i-1][j][k][1])%p;//和不相等的情况一样
f[i][j][k][1] = ((f[i-1][j-1][k][1] + f[i-1][j-1][k-1][0]) % p + f[i-1][j-1][k-1][1])%p;
最后的答案就是 f[n][m][k][0] + f[n][m][k][1]
由于我们四维的数组开不下这么大,所以我们可以来滚动第一维(因为第一维只与 i-1 位有关)
Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int p = 1e9+7;
int n,m,kk,ans,f[2][220][220][2];
char a[1010],b[220];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
int main()
{
n = read(); m = read(); kk = read();
scanf("%s",a+1); scanf("%s",b+1);
f[0][0][0][0] = 1;//初始化
for(int i = 1, now = 1; i <= n; i++, now ^= 1)//滚动第一维
{
f[now][0][0][0] = 1;
for(int j = 1; j <= m; j++)
{
for(int k = 1; k <= kk; k++)
{
if(a[i] != b[j]) //不相等的情况
{
f[now][j][k][0] = (f[now ^ 1][j][k][0] + f[now ^ 1][j][k][1])%p;
f[now][j][k][1] = 0;
}
if(a[i] == b[j])
{
f[now][j][k][0] = (f[now ^ 1][j][k][0] + f[now ^ 1][j][k][1])%p;
f[now][j][k][1] = ((f[now ^ 1][j-1][k][1] + f[now ^ 1][j-1][k-1][0]) % p + f[now ^ 1][j-1][k-1][1])%p;//他自己和前一个字符串在同一个集合的情况 f[now ^ 1][j-1][k][1]
//他自己新开一个集合,但前一个字符不在上一个集合的情况 f[now ^ 1][j-1][k-1][0], 在上一个集合的情况 f[now ^ 1][j-1][k-1][1]
}
}
}
}
ans = (f[n & 1][m][kk][0] + f[n & 1][m][kk][1])%p;
printf("%d\n",ans);
return 0;
}