题解:[NOIP 2015 提高组] 子串

题目传送门

DP 状态设计

因为需要满足无后效性原则,因此 DP 考虑的肯定是两段前缀。

\(f_{i,j,p,0/1}\) 表示从 \(A\) 中取前 \(i\) 个字符(第 \(i\) 个字符选或不选)取 \(p\) 段,匹配 \(B\) 中前 \(j\) 个字符的方案数。

DP 状态转移

  • \(A_i=B_j\) 时。

    显然,有 \(f_{i,j,p,0}=f_{i-1,j,p,0}+f_{i-1,j,p,1}\)

    还有:\(f_{i,j,p,1}=f_{i-1,j-1,p,0}+f_{i-1,j-1,p-1,0}+f_{i-1,j-1,p-1,1}\)

    • \(f_{i-1,j-1,p,0}\):将 \(A_i\) 加入至 \(A_{i-1}\) 所在子串的末尾。

    • \(f_{i-1,j-1,p-1,0/1}\):将 \(A_i\) 单独作为一个子串。

  • \(A_i\ne B_j\) 时。

    显然,有 \(f_{i,j,p,1}=0\),因为不可能存在这种情况。

    \(f_{i,j,p,0}=f_{i-1,j,p,0}+f_{i-1,j,p,1}\),因为 \(A_i\) 选不进去,如果选进去只能在末尾,但是并不成立。

滚动数组优化

我们开的 \(f\) 数组实际上会达到 \(\mathcal O(nmk)=\mathcal O(8\times10^7)\) 的量级,会 \(\text{MLE}\)

因此考虑优化,可以滚动数组滚掉一维。

AC 代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=1000,M=200,K=200,P=1000000007;
int n,m,k;
char a[N+1],b[M+1];
int f[2][M+1][K+1][2];
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d %d %d",&n,&m,&k);
	scanf("%s%s",a+1,b+1);
	f[0][0][0][0]=f[1][0][0][0]=1;
	bool mode=0;
	for(int i=1;i<=n;i++){
		mode=!mode;
		for(int j=1;j<=m;j++){
			for(int kk=1;kk<=k;kk++){
				if(a[i]==b[j]){
					f[mode][j][kk][0]=(f[!mode][j][kk][0]+f[!mode][j][kk][1])%P;
					f[mode][j][kk][1]=(1ll*f[!mode][j-1][kk][1]+f[!mode][j-1][kk-1][0]+f[!mode][j-1][kk-1][1])%P;
				}else{
					f[mode][j][kk][0]=(f[!mode][j][kk][0]+f[!mode][j][kk][1])%P;
					f[mode][j][kk][1]=0;
				}
			}
		}
	}
	printf("%d\n",(f[mode][m][k][0]+f[mode][m][k][1])%P);
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-07-21 21:40  TH911  阅读(10)  评论(0)    收藏  举报