【KMP】【字符串】BZOJ4560字符串覆盖

分析:

这题与KMP唯一的关系就是个判断。。。

首先,利用KMP算出哪些位置可以匹配。

然后显然有个DP[i][j]表示前i个字符,凑出的状态为j。为了处理相交情况,所以每次向后转移都必须枚举很多位置。然后可以利用树状数组优化,DP[i]=DP[j]+i-j这种形式的DP,可以先把DP[j]-j存下来,然后读取的时候直接读该位置的最大/最小值+i即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define SF scanf
#define PF printf
#define MAXN 10010
using namespace std;
char s[MAXN],s1[5][MAXN];
int len[5];
int fail[MAXN];
int m,n;
int tr1[20][MAXN],tr2[20][MAXN];
int sg[MAXN][20],sf[MAXN][20],f[MAXN][20],g[MAXN][20];
bool vis[MAXN];
int ch[MAXN];
void build(int x){
	int i=0,j=-1;
	fail[0]=-1;
	while(i<len[x]){
		if(j==-1||s1[x][i]==s1[x][j]){
			i++;
			j++;
			fail[i]=j;	
		}
		else
			j=fail[j];
	}
}
void solve(int x){
	int i=0,j=0;
	while(i<m){
		if(j==-1||s[i]==s1[x][j]){
			i++;
			j++;
		}
		else
			j=fail[j];
		if(j==len[x])
			ch[i-len[x]]|=(1<<(x-1));
	}
}
int que1(int id,int i){
	i++;
	int res=tr1[id][0];
	for(;i<=m+1;i+=i&(-i))
		res=max(res,tr1[id][i]);
	return res;
}
int que2(int id,int i){
	i++;
	int res=tr2[id][0];
	for(;i<=m+1;i+=i&(-i))
		res=min(res,tr2[id][i]);
	return res;
}
void add1(int id,int i,int v){
	i++;
	for(;i;i-=i&(-i))
		tr1[id][i]=max(tr1[id][i],v);
}
void add2(int id,int i,int v){
	i++;
	for(;i;i-=i&(-i))
		tr2[id][i]=min(tr2[id][i],v);
}
int main(){
	int t;
	SF("%d",&t);
	while(t--){
		SF("%s",s);
		m=strlen(s);
		SF("%d",&n);
		memset(ch,0,sizeof ch);
		for(int i=1;i<=n;i++){
			SF("%s",s1[i]);
			len[i]=strlen(s1[i]);
			memset(fail,0,sizeof fail);
			build(i);
			solve(i);
		}
		memset(vis,0,sizeof vis);
		for(int a=1;a<=n;a++)
			for(int b=1;b<=n;b++)
				if(a!=b&&vis[a]==0&&vis[b]==0){
					memset(fail,0,sizeof fail);
					fail[0]=-1;
					int j=-1;
					int i=0;
					while(i<len[a]){
						if(j==-1||s1[a][i]==s1[a][j]){
							i++;
							j++;
							fail[i]=j;	
						}
						else
							j=fail[j];
					}
					i=j=0;
					while(i<len[b]){
						if(j==-1||s1[b][i]==s1[a][j]){
							i++;
							j++;	
						}
						else
							j=fail[j];
						if(j==len[a]){
							vis[a]=1;
							break;
						}
					}
				}
			int mask=(1<<n)-1;
			for(int i=1;i<=n;i++)
				if(vis[i]==1)
					mask^=1<<(i-1);
			memset(tr1,0xc0,sizeof tr1);
			memset(tr2,0x3f,sizeof tr2);
			memset(sf,0xc0,sizeof sf);
			memset(sg,0x3f,sizeof sg);
			memset(f,0xc0,sizeof f);
			memset(g,0x3f,sizeof g);
			for(int i=0;i<(1<<n);i++){
				f[0][i]=0xc0;
				g[0][i]=0x3f;
			}
			for(int i=0;i<=m;i++)
				for(int j=0;j<(1<<n);j++){
					if(i==0){
						if(j==0)
							f[0][0]=g[0][0]=sf[0][0]=sg[0][0]=0;
						else{
							f[0][j]=que1(j,i)+i;	
							g[0][j]=que2(j,i)+i;	
						}
					}
					else{
						sf[i][j]=max(sf[i][j],sf[i-1][j]);
						sg[i][j]=min(sg[i][j],sg[i-1][j]);	
						f[i][j]=max(sf[i][j],que1(j,i)+i);
						g[i][j]=min(sg[i][j],que2(j,i)+i);
					}
					for(int k=1;k<=n;k++)
						if((j&(1<<(k-1)))==0&&(ch[i]&(1<<(k-1)))){
							int mask1=(j|(1<<(k-1)));
							add1(mask1,i+len[k],f[i][j]-i);
							sf[i+len[k]][mask1]=max(sf[i+len[k]][mask1],f[i][j]+len[k]);
							if(vis[k]==0){
								add2(mask1,i+len[k],g[i][j]-i);
								sg[i+len[k]][mask1]=min(sg[i+len[k]][mask1],g[i][j]+len[k]);
							}
						}
				}
		int ans=0;
		//PF("[%d]",mask);
		for(int i=0;i<(1<<n);i++)
			if((i&mask)==mask)
				ans=max(ans,f[m][i]);
		PF("%d %d\n",g[m][mask],ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值