#149. 【NOIP2015】子串
统计有两个仅包含小写英文字母的字符串 A 和 B 。
现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 B 相等?
注意:子串取出的位置不同也认为是不同的方案。
输入格式
第一行是三个正整数 n,m,k ,分别表示字符串 A 的长度,字符串 B 的长度,以及问题描述中所提到的 k ,每两个整数之间用一个空格隔开。
第二行包含一个长度为 n 的字符串,表示字符串 A 。
第三行包含一个长度为 m 的字符串,表示字符串 B 。
输出格式
输出共一行,包含一个整数,表示所求方案数。
由于答案可能很大,所以这里要求输出答案对 1000000007 取模的结果。
样例一
input
6 3 1 aabaab aab
output
2
样例二
input
6 3 2 aabaab aab
output
7
样例三
input
6 3 3 aabaab aab
output
7
explanation
所有合法方案如下:(加下划线的部分表示取出的子串)
样例一:aab aab / aab aab
样例二:a ab aab / a aba ab / a a ba ab / aab a ab / aa b aab / aa baa b / aab aa b
样例三:a a b aab / a a baa b / a ab a a b / a aba a b / a a b a a b / a a ba a b / aab a a b
限制与约定
测试点编号 | n 的规模 | m 的规模 | k 的规模 |
---|---|---|---|
1 | n≤500 | m≤50 | k=1 |
2 | k=2 | ||
3 | |||
4 | k=m | ||
5 | |||
6 | k≤m | ||
7 | |||
8 | n≤1000 | m≤100 | |
9 | |||
10 | m≤200 |
时间限制: 1s
空间限制: 128MB
设f[i][j][k][0/1]表示A串的前i位取出k个子串匹配了B串的前j位,选或不选第i位。
分两种情况1.若A串第i位与B串第j位匹配,那么f[i][j][k][1]=f[i-1][j-1][k][1]+f[i-1][j-1][k-1][0]+f[i-1][j-1][k-1][1].
解释一下:若匹配,则选择这一位的情况有如下几种累加方式:(首先B串的匹配个数都要-1)前一位也选并与当前位构成同一个子串(子串个数不变)、前一位选并且不断点为这一位、前一位不选(子串个数都要-1)。
f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1]. 解释一下:若不选择这一位,当前方式可以累加的有:(B串的匹配个数不变)前一位选或者前一位不选,子串个数不变(因为这一位不选)
2.若A串第i位与B串第j位不匹配,则f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1].解释:若不匹配,该位肯定不能选。所以不选的方案数累加上前一位选或者不选的个数。
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
const int MAXN=1010;
const int MAXM=220;
const int Mod=1e9+7;
long long f[2][MAXM][MAXM][2];
int main()
{
char a[MAXN],b[MAXM];
int n,m,k,cnt=0,last=-1;
scanf("%d%d%d",&n,&m,&k);
scanf("%s",a+1);
scanf("%s",b+1);
f[0][0][0][0]=1;
for (int i=1;i<=n;i++)
{
cnt++;
cnt%=2;
last++;
last%=2;
for (int j=0;j<=m;j++)
{
for (int p=0;p<=k;p++)
{
if (a[i]==b[j])
{
if (j>0)
{
if (p>0)
{
f[cnt][j][p][1]=f[last][j-1][p][1]+f[last][j-1][p-1][0]+f[last][j-1][p-1][1];
f[cnt][j][p][1]%=Mod;
}
}
f[cnt][j][p][0]=f[last][j][p][0]+f[last][j][p][1];
f[cnt][j][p][0]%=Mod;
}
else
{
f[cnt][j][p][0]=f[last][j][p][0]+f[last][j][p][1];
f[cnt][j][p][0]%=Mod;
}
}
}
for (int j=0;j<=m;j++)
{
for (int p=0;p<=k;p++)
{
f[last][j][p][0]=0;
f[last][j][p][1]=0;
}
}
}
cout<<(f[n%2][m][k][1]+f[n%2][m][k][0])%Mod;
return 0;
}
需要开滚动数组不然会MLE,滚动数组每用完一次后都要把当前清零。
再次吐槽超多细节(NOIP还是坑人啊)