SPOJ - PHRASES (Relevant Phrases of Annihilation)

题意:给定 n 个字符串,找到最长的一个子串 s,使得 s 在每个字符串中至少不重叠出现两次,求出这个长度;

 

分析:将 n 个字符串连接起来,两两之间加上不相同未在正文出现的字符,求一遍后缀数组,然后二分这个最大子串长度判断就好了

 

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 2E5+10;

char s[N];
int n,sa[N],rk[N],oldrk[N<<1],id[N],px[N],cnt[N],ht[N];
int str[N];

void init(){
	memset(cnt,0,sizeof(cnt));
}

bool cmp(int x,int y,int w){
	return oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[y+w];
}

void da(int s[],int n,int m){
	int i,p=0,w,k;
	for(i=1;i<=n;++i) ++cnt[rk[i] = s[i]];
	for(i=1;i<=m;++i) cnt[i] += cnt[i-1];
	for(i=n;i>=1;--i) sa[cnt[rk[i]]--] = i;
	
	for(w=1;w<n;w<<=1,m=p){ //m=p:优化计数排序值域 
		for(p=0,i=n;i>n-w;--i) id[++p]=i;
		for(i=1;i<=n;++i)
		    if(sa[i]>w) id[++p]=sa[i]-w;
		memset(cnt,0,sizeof(cnt));
		for(i=1;i<=n;++i) ++cnt[px[i] = rk[id[i]]];
		for(i=1;i<=m;++i) cnt[i] += cnt[i-1];
		for(i=n;i>=1;--i) sa[cnt[px[i]]--] = id[i];
		memcpy(oldrk,rk,sizeof(rk));
		
		for(p=0,i=1;i<=n;++i)
            rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
	}
	
	for(i=1,k=0;i<=n;++i){
		if(k) --k;
		while(s[i+k]==s[sa[rk[i]-1]+k]) ++k;
		ht[rk[i]]=k;
	}
}

int num[15],pre[15],idx[N];

int abs(int x){
	return x<0?-x:x;
}

bool judge(int n){
	for(int i=1;i<=n;i++){
		if(num[i]<2) return 0;
	}
	return 1;
}

bool check(int len,int cnt,int n){
	memset(num,0,sizeof(num));
	memset(pre,-1,sizeof(pre));
	for(int i=1;i<=cnt;i++){
		if(ht[i]>=len){
			if(pre[idx[sa[i-1]]]==-1){
				num[idx[sa[i-1]]]++;
				pre[idx[sa[i-1]]]=sa[i-1];
			}
			else if(abs(pre[idx[sa[i-1]]]-sa[i-1])>=len){
				num[idx[sa[i-1]]]++;
			}
			
			if(pre[idx[sa[i]]]==-1){
				num[idx[sa[i]]]++;
				pre[idx[sa[i]]]=sa[i];
			}
			else if(abs(pre[idx[sa[i]]]-sa[i])>=len){
				num[idx[sa[i]]]++;
			}
		}
		else{
			if(judge(n)) return 1;
			memset(num,0,sizeof(num));
	        memset(pre,-1,sizeof(pre));
		}
	}
	return judge(n);
}

int main()
{
	int T,i,j,l,r,cnt,len,ANS; 
	scanf("%d",&T);
	while(T--){
		init();
		scanf("%d",&n);
		cnt=0,r=1E7;
		for(i=1;i<=n;i++){
			scanf("%s",s);
			len=strlen(s);
			r=min(r,len);
			for(j=0;j<len;j++){
				idx[++cnt]=i;
				str[cnt]=s[j];
			}
			idx[++cnt]=0;
			str[cnt]=i;
		}
		cnt--;
		da(str,cnt,130);
		l=1,r/2,ANS=0;
	    while(l<=r){
		    int m=(l+r)>>1;
		    if(check(m,cnt,n)){
			    ANS=m;
			    l=m+1;
		    }
		    else r=m-1;
	    } 
	    printf("%d\n",ANS);
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值