CF1729G Cut Substrings

最小删除次数使子串消失
文章讲述了如何解决一个字符串问题,给定两个非空字符串s和t,目标是最小次数删除s中的t,使得s中不再包含t。通过暴力求解位置和状态转移数组f和g,计算最小删除次数及其方案数,时间复杂度为O(n*m+n^2),其中n和m分别是s和t的长度。

CF1729G Cut Substrings

题目大意

给出两个非空字符串 s s s t t t,每次可以将字符串 s s s中的任意一个字符串 t t t删除,求最小次数删除使得字符串 s s s中不会出现字符串 t t t,并求出有多少种不同的方案。

如果删除的序列相同而顺序不同,也看作相同的方案。如 { 3 , 5 } \{3,5\} {3,5} { 5 , 3 } \{5,3\} {5,3}是相同的方案。

输出最小删除次数与以最小删除次数删除的方案数对 1 0 9 + 7 10^9+7 109+7取模后的值。

q q q组数据。
1 ≤ q ≤ 50 1\leq q\leq 50 1q50 1 ≤ ∑ ∣ s ∣ ≤ 500 1\leq \sum|s|\leq 500 1s500 1 ≤ ∑ ∣ t ∣ ≤ 500 1\leq \sum|t|\leq 500 1t500


题解

s s s的长度为 n n n t t t的长度为 m m m

首先要求出 t t t s s s中出现的位置。可以用KMP,但没有必要,暴力求的总时间复杂度为 O ( ∑ ( n × m ) ) O(\sum(n\times m)) O((n×m)),而 ∑ ( n × m ) ≤ ( ∑ n ) × ( ∑ m ) ≤ 250000 \sum(n\times m)\leq (\sum n)\times (\sum m)\leq 250000 (n×m)(n)×(m)250000,完全是可以的。

令各个位置分别为 a 1 , a 2 , … , a k a_1,a_2,\dots,a_k a1,a2,,ak k = 0 k=0 k=0需要特判,输出0,1

f i f_i fi表示让前 i i i个位置都不存在的最小删除次数, g i g_i gi表示其方案数。首先删去的两段不能有交集,令 k k k满足 a k > a i + m − 1 a_k>a_i+m-1 ak>ai+m1的最小的数,则转移为 f j = min ⁡ ( f j , f i + 1 ) f_j=\min(f_j,f_i+1) fj=min(fj,fi+1),其中 j > i j>i j>i a j ≤ a k + m − 1 a_j\leq a_k+m-1 ajak+m1

注意 i = 1 i=1 i=1时,对于满足 a j ≤ a i + m − 1 a_j\leq a_i+m-1 ajai+m1 j j j f j = 1 f_j=1 fj=1

在求 f f f时顺便求 g g g即可,具体见代码。

时间复杂度为 O ( ∑ ( n × m ) + ∑ n 2 ) O(\sum(n\times m)+\sum n^2) O((n×m)+n2)

code

#include<bits/stdc++.h>
using namespace std;
int T,s1,t1,a1,a[505];
long long ans1,ans2,f[505],g[505];
long long mod=1000000007;
char s[505],t[505];
void gt(){
	for(int i=1;i<=s1-t1+1;i++){
		int j=1;
		for(j=1;j<=t1&&s[i+j-1]==t[j];j++);
		if(j==t1+1) a[++a1]=i;
	}
}
int main()
{
	scanf("%d",&T);
	while(T--){
		scanf("%s%s",s+1,t+1);
		s1=strlen(s+1);
		t1=strlen(t+1);
		a1=0;gt();
		if(a1==0){
			printf("0 1\n");
			continue;
		}
		for(int i=1;i<=a1;i++){
			f[i]=1e9;g[i]=0; 
		}
		for(int j=1;a[j]<=a[1]+t1-1&&j<=a1;j++){
			if(f[j]>1){
				f[j]=1;
				g[j]=1;
			}
			else if(f[j]==1){
				g[j]=(g[j]+g[1])%mod;
			}
		}
		for(int i=1;i<=a1;i++){
			int lst=i+1;
			while(a[lst]<=a[i]+t1-1&&lst<=a1) ++lst;
			if(lst>a1) continue;
			for(int j=lst;a[j]<=a[lst]+t1-1&&j<=a1;j++){
				if(f[j]>f[i]+1){
					f[j]=f[i]+1;
					g[j]=g[i];
				}
				else if(f[j]==f[i]+1){
					g[j]=(g[j]+g[i])%mod;
				}
			}
		}
		ans1=1e9;ans2=0;
		for(int j=a1;a[j]+t1-1>=a[a1]&&j>=1;j--){
			if(f[j]<ans1){
				ans1=f[j];
				ans2=g[j];
			}
			else if(ans1==f[j]){
				ans2=(ans2+g[j])%mod;
			}
		}
		printf("%lld %lld\n",ans1,ans2);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值