[Noip2015]子串
[Noip2015]子串
一.前言
呜呜呜呜我不要写字符串哇哇哇……题目链接
二.思路
其实跟某诡异字符串算法差不多,\(f_{i,j,k}\) 表示在 a 中前 i 个字符取出 k 个子串完美匹配了 \(b[1...j]\) 的方案数,但是这题为了方便转移子串数,最好加一维 0/1 表示 \(a_i\) 有没有用。
设出来状态就很好做了,分情况讨论8~。
首先是 \(f_{i,j,k,0}\) ,反正当前位都不选,k 和 j 都不会增加,前一个的抉择都无所谓了,直接继承
\[f_{i,j,k,0}=f_{i-1,j,k,1}+f_{i-1,j,k,0}
\]
然后是 \(f_{i,j,k,1}\) ,既然要选,先要看 \(a_i\) 能不能匹配 \(b_j\) 不能匹配直接白给,方案为 0.如果可以匹配的话,接上就好,此时既可以自成一段亦可以和前一个接起来。
\[f_{i,j,k,1}=f_{i-1,j-1,k,1}+f_{i-1,j-1,k-1,1}+f_{i-1,j-1,k-1,0}
\]
但是内存会爆炸,考虑到每次只用了前一个,\(\mod 2\) 滚掉一维就行。
三.CODE
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<fstream>
#include<cmath>
using namespace std;
int read(){
char ch=getchar();
int res=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0');
return res*f;
}
const int mod=1000000007;
int n,m,K,sum,f[1005][205][205][2];
char a[1005],b[205];
int main(){
scanf("%d%d%d\n",&n,&m,&K);
for(int i=1;i<=n;++i){
f[i][0][0][0]=1;
scanf("%c",&a[i]);
}
f[0][0][0][0]=1;
scanf("\n");
for(int i=1;i<=m;++i)scanf("%c",&b[i]);
for(int i=1;i<=n;++i){
int now=i%2,pre=(i-1)%2;
for(int j=1;j<=m&&j<=i;++j){
for(int k=1;k<=K&&k<=j;++k){
f[now][j][k][0]=(f[pre][j][k][0]+f[pre][j][k][1])%mod;
if(a[i]==b[j]){
f[now][j][k][1]=((f[pre][j-1][k-1][0]+
f[pre][j-1][k][1])%mod+f[pre][j-1][k-1][1])%mod;
}
else f[now][j][k][1]=0;
}
}
}
cout<<(f[n%2][m][K][0]+f[n%2][m][K][1])%mod;
return 0;
}