原题目:P2679 [NOIP2015 提高组] 子串 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目背景
NOIP2015 Day2T2
题目描述
有两个仅包含小写英文字母的字符串 A 和 B。
现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 B 等?
注意:子串取出的位置不同也认为是不同的方案。
输入格式
第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问题描述中所提到的 k,每两个整数之间用一个空格隔开。
第二行包含一个长度为 n 的字符串,表示字符串 A。
第三行包含一个长度为 m 的字符串,表示字符串 B。
输出格式
一个整数,表示所求方案数。
由于答案可能很大,所以这里要求输出答案对 1000000007取模的结果。
说明/提示
数据范围
对于第 1 组数据:1≤n≤500,1≤m≤50,k=1;
对于第 2 组至第 3 组数据:1≤n≤500,1≤m≤50,k=2;
对于第 4 组至第 5 组数据:1≤n≤500,1≤m≤50,k=m;
对于第 1 组至第 7 组数据:1≤n≤500,1≤m≤50,1≤k≤m;
对于第 1 组至第 9 组数据:1≤n≤1000,1≤m≤100,1≤k≤m;
对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。
90分三维数组
f[i][j][x]表示从A字符串中拿出i个数组合成x个子串再组成B字符串的前j个数(i<=n,j<=m,x<=k)
当ai != bj时,f[i][j][x]=f[i-1][j][x],即不选这个数,将前一个数的方案数传递
当ai == bj时,比对从i的位置的数起,如果前y个数能一一匹配的数的方案数 (ai-1 == bj-1),那就将它们的值累加起来,赋值给f[i][j][x],再加上不选这个数的方案数
Ac代码如下
#include <bits/stdc++.h>
#define mo 1000000007
using namespace std;
int n,m,k,sum;
string a,b;
int f[1005][105][105];
int main ()
{
int i,j,x,y;
cin>>n>>m>>k;
cin>>a>>b;
a=" "+a;
b=" "+b;
f[0][0][0]=1;
for (i=1;i<=n;i++)
{
for (j=0;j<=m;j++)
{
for (x=0;x<=k;x++)
{
int sum=0;
if (a[i]==b[j])
{
for (y=1;y<=j && i-y>=0;y++)
{
if (a[i-y+1]==b[j-y+1]) sum=(f[i-y][j-y][x-1]+sum)%mo;
else break;
}
}
f[i][j][x]=(f[i-1][j][x]+sum)%mo;
}
}
}
cout<<f[n][m][k];
return 0;
}
100分二维数组
因为f[i][j][x]为前y个数累加的值,即
f[i][j][x]=f[i-1][j-1][x-1] + f[i-2][j-2][x-1] + ……
若ai+1 == bj+1,那么
f[i+1][j+1][x]=f[i][j][x-1] + f[i-1][j-1][x-1] + ……
我们发现,在上面的两个式子中有一串个相等的值 f[i-1][j-1][x-1] + f[i-2][j-2][x-1] + ……,我们用一个数组 sum[i][j][x] 记录下它,就节省了一次循环,我们再将sum[i][j][x]代入上方的式子
如果ai == bj,那么
sum[i][j][x]=sum[i-1][j-1][x] + f[i-1][j-1][x-1]
f[i][j][x]=f[i-1][j][x] + sum[i][j][x]
如果ai != bj,那么
sum[i][j][x]=0
f[i][j][x]=f[i-1][j][x]
由上面的式子可以看出,i只与i-1有关,与i-2 ……无关,所以我们可以将i优化
Ac代码如下
#include <bits/stdc++.h>
#define mo 1000000007
using namespace std;
int n,m,k;
string a,b;
int f[2][205][205],sum[2][205][205];
int main ()
{
int i,j,x;
cin>>n>>m>>k;
cin>>a>>b;
a=" "+a;
b=" "+b;
f[0][0][0]=1;
for (i=1;i<=n;i++)
{
for (j=0;j<=m;j++)
{
for (x=0;x<=k;x++)
{
if (a[i]==b[j]) sum[i%2][j][x]=(sum[(i+1)%2][j-1][x]+f[(i+1)%2][j-1][x-1])%mo;
else sum[i%2][j][x]=0;
f[i%2][j][x]=(f[(i+1)%2][j][x]+sum[i%2][j][x])%mo;
}
}
}
cout<<f[n%2][m][k]%mo;
return 0;
}
这里我用i%2和(i+1)%2来切换i与i-1层,近似可以看作两个一维数组,或者可以把他当作01背包,倒序循环来切换,便于理解我就写为上方的代码
成功Ac!!!
制作不易请多多点赞!!!