[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;
} 
posted @ 2020-08-12 22:28  clockwhite  阅读(133)  评论(1编辑  收藏  举报