CCPC2022 网络赛 Substring Match

Substring Match

给定长为\(n\)的文本串\(S\)和长为\(m\)的模式串\(T\),求\(T\)\(S\)中能匹配的最长的子串的长度。

\(T\)中有不超过200个大写字母。大写字母能匹配任意数量(包括\(0\))个对应的小写字母。

\(1\leq n,m\leq 10^5\)

题解

这个大写字母的匹配显然不能贪心。

考虑到大写字母数量比较少,可以将\(T\)按照单个大写字母和连续小写字母分段,然后DP。也就是把这个是否贪心的判断交给程序。

\(f(i, j)\)表示\(S\)的第\(i\)个位置匹配到\(T\)的第\(j\)段能匹配的最大长度。

为了\(O(1)\)转移,我们需要预处理每个连续小写字母段在\(S\)中能匹配的位置。

时间复杂度\(O(200n)\)

constexpr int N=1e5+10, INF=1e9;
char s[N], t[N], r[N];

struct Node{
	int type, l, r; // 1 for abc, 2 for A
}a[410];

int mt[410][N], nxt[N];

void match(int n, int m, int k){
	fill(mt[k], mt[k]+n+1, 0);
	for(int i=2, j=0; i<=m; ++i){
		while(j && r[i]!=r[j+1]) j=nxt[j];
		if(r[i]==r[j+1]) ++j;
		nxt[i]=j;
	}
	for(int i=1, j=0; i<=n; ++i){
		while(j && (j==m || s[i]!=r[j+1])) j=nxt[j];
		if(s[i]==r[j+1]) ++j;
		if(j==m) mt[k][i-m+1]=1;
	}
}

int f[N][410];

void cmax(int &x, int y){
	x=max(x, y);
}
void realMain(){
	scanf("%s%s", s+1, t+1);
	int n=strlen(s+1), m=strlen(t+1), cnt=0;
	for(int l=1, r; l<=m; l=r+1){
		if(isupper(t[l])){
			r=l;
			a[++cnt]={2, l, r};
			continue;
		}
		for(r=l; r+1<=m && islower(t[r+1]); ++r);
		a[++cnt]={1, l, r};
	}
	for(int i=1; i<=cnt; ++i)if(a[i].type==1){
		copy(t+a[i].l, t+a[i].r+1, r+1);
		match(n, a[i].r-a[i].l+1, i);
	}
	
	for(int i=0; i<=n; ++i) fill(f[i]+1, f[i]+cnt+1, -INF);
	for(int i=0; i<=n; ++i)
		for(int j=0; j<=cnt; ++j)if(f[i][j]>-INF){
			if(i<n && a[j].type==2 && s[i+1]==tolower(t[a[j].l]))
				cmax(f[i+1][j], f[i][j]+1);
			if(j<cnt && a[j+1].type==2)
				cmax(f[i][j+1], f[i][j]);
			if(i<n && j<cnt && a[j+1].type==1 && mt[j+1][i+1])
				cmax(f[i+a[j+1].r-a[j+1].l+1][j+1], f[i][j]+a[j+1].r-a[j+1].l+1);
			if(i<n && j<cnt && a[j+1].type==2 && s[i+1]==tolower(t[a[j+1].l]))
				cmax(f[i+1][j+1], f[i][j]+1);
		}
	
	int ans=0;
	for(int i=1; i<=n; ++i) cmax(ans, f[i][cnt]);
	printf("%d\n", ans);
}
int main(){
	for(int t=read<int>(); t--; ) realMain();
	return 0;
}

考试时写代码出了一系列问题。比如不会写KMP,f[i][0]初值设成-INFans初值设成-INF

posted on 2022-09-18 11:16  autoint  阅读(86)  评论(0编辑  收藏  举报

导航