后缀数组基本问题QAQ

以下题目均来自罗穗骞的论文...

No.1最长公共前缀

最长公共前缀:

题目:

给定一个字符串,询问某两个后缀的最长公共前缀。

分析:

某两个后缀的最长公共前缀就是区间height最小值,转化为RMQ问题,nlgn预处理,O(1)询问...

No.2单个字符串的相关问题

1、重复子串

可重叠最长重复子串:

题目:

给定一个字符串,求最长重复子串,这两个子串可以重叠。

分析:

就是height数组的最大值...

不可重叠最长重复子串(POJ1743)

题目:

给定一个字符串,求最长重复子串,这两个子串不能重叠。

这道题有一个特殊要求是如果一个子串的每个数同时加上一个相同的数字所得到的子串也在字符串中出现的话也是重复子串...

分析:

我们先忽略特殊要求只考虑普通的重复子串...

首先我们通过二分答案把最优性问题转化为可行性问题...

我们二分最长的重复字串的长度为len,然后我们根据len把sa按照height数组分组,我们把sa分成若干组,保证每一组的height最小值都大于等于len,这样就保证了组内的最长公共前缀是大于等于len的,现在要求不重复,就是要求最长公共前缀大于等于len的两个后缀的开头距离是大于等于len的,这样就保证了不重复...

现在考虑特殊要求,因为整体加数相同的话说,差分之后也应该相同,所以我们把n个数通过差分变成n-1个数,其他的照旧,只不过开头距离大于len的才合法...(因为是差分之后的...)

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;
//良辰美景奈何天,赏心乐事谁家院 

const int maxn=200000+5;

int n,s[maxn],gs[maxn],wv[maxn],wb[maxn],sa[maxn],rank[maxn],height[maxn]; 

inline int read(void){
	char ch=getchar();int x=0;
	while(!(ch>='0'&&ch<='9'))
		ch=getchar();
	while(ch>='0'&&ch<='9')
		x=x*10+ch-'0',ch=getchar();
	return x;
}

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}

inline void calheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) rank[sa[i]]=i;
	for(i=0;i<n;height[rank[i++]]=k)
		for(k?k--:233,j=sa[rank[i]-1];s[i+k]==s[j+k];k++);
}

inline bool check(int x){
	int Max=0,Min=n;bool ans=0;
	for(int i=2;i<=n&&ans==0;i++){
		if(height[i]<x){
			if(Max!=0)
				if(Max-Min>x)
					ans=1;
			Min=Max=sa[i];
			continue;
		}
		Max=max(Max,sa[i]),Min=min(Min,sa[i]);
	}
	if(Max!=0&&Max-Min>x)
		ans=1;
	return ans;
}

signed main(void){
	while(n=read()){
		memset(s,0,sizeof(s));
		memset(gs,0,sizeof(gs));
		memset(wb,0,sizeof(wb));
		memset(wv,0,sizeof(wv));
		memset(sa,0,sizeof(sa));
		memset(rank,0,sizeof(rank));
		memset(height,0,sizeof(height));
		for(int i=0;i<n;i++)
			s[i]=read();
		if(n<10){
			puts("0");continue;
		}
		n--;
		for(int i=0;i<n;i++)
			rank[i]=s[i]=s[i+1]-s[i]+100;
		s[n]=0;
		da(sa,rank,n+1,200);calheight(n);
		int l=4,r=n>>1,ans=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(check(mid))
				ans=mid,l=mid+1;
			else
				r=mid-1;
		}
		printf("%d
",ans+1);
	}
	return 0;
}//Cap ou pas cap. Pas cap.

可重叠的k次最长重复子串(POJ3261)

题意:

给定一个字符串,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠。

分析:

和上一个题一样还是利用了二分和分组的思想,我们二分答案,然后按照二分的答案分组,如果存在一组连续后缀大于等于k个就代表合法...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=20000+5;

int n,len,s[maxn],gs[maxn],sa[maxn],wb[maxn],wv[maxn],ran[maxn],height[maxn];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}

inline bool check(int x){
	int cnt=0;
	for(int i=1;i<=n;i++){
		if(height[i]<x){
			if(cnt>=len)
				return true;
			cnt=1;continue;
		}
		cnt++;
	}
	if(cnt>=len)
		return true;
	return false;
}

signed main(void){
	scanf("%d%d",&n,&len);
	for(int i=0;i<n;i++)
		scanf("%d",&s[i]),ran[i]=s[i];
	da(sa,ran,n+1,n+1),calheight(n);
	int l=1,r=n,ans=0;
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid))
			ans=mid,l=mid+1;
		else
			r=mid-1;
	}
	printf("%d
",ans);
	return 0;
}//Cap ou pas cap. Pas cap.

 2.子串的个数

 不相同的子串个数(spoj694,spoj705)

题目:

给定一个字符串,求不相同的子串的个数。

分析:

我们考虑每次新加入下一个后缀的时候产生的子串的贡献是n-sa[i],其中和上一个重复的是height[i]个,所以当前的后缀对答案产生的贡献是n-sa[i]-height[i]...

代码:

SPOJ694

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=1000+5;

int n,cas,s[maxn],gs[maxn],sa[maxn],wb[maxn],wv[maxn],ran[maxn],height[maxn];

char str[maxn];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}

inline int solve(int n){
	int ans=0;
	for(int i=1;i<=n;i++)
		ans+=n-sa[i]-height[i];
	return ans;
}

signed main(void){
	scanf("%d",&cas);
	while(cas--){
		memset(s,0,sizeof(s));
		memset(gs,0,sizeof(gs));
		memset(sa,0,sizeof(sa));
		memset(wb,0,sizeof(wb));
		memset(wv,0,sizeof(wv));
		memset(ran,0,sizeof(ran));
		memset(height,0,sizeof(height));
		scanf("%s",str);n=strlen(str);
		for(int i=0;i<n;i++) 
			s[i]=(int)str[i]+1,ran[i]=s[i];
		da(sa,ran,n+1,300);calheight(n);
		printf("%d
",solve(n));
	}
	return 0;
}//Cap ou pas cap. Pas cap.

SPOJ705

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=50000+5;

int n,cas,s[maxn],gs[maxn],sa[maxn],wb[maxn],wv[maxn],ran[maxn],height[maxn];

char str[maxn];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}

inline int solve(int n){
	int ans=0;
	for(int i=1;i<=n;i++)
		ans+=n-sa[i]-height[i];
	return ans;
}

signed main(void){
	scanf("%d",&cas);
	while(cas--){
		memset(s,0,sizeof(s));
		memset(gs,0,sizeof(gs));
		memset(sa,0,sizeof(sa));
		memset(wb,0,sizeof(wb));
		memset(wv,0,sizeof(wv));
		memset(ran,0,sizeof(ran));
		memset(height,0,sizeof(height));
		scanf("%s",str);n=strlen(str);
		for(int i=0;i<n;i++) 
			s[i]=(int)str[i]+1,ran[i]=s[i];
		da(sa,ran,n+1,300);calheight(n);
		printf("%d
",solve(n));
	}
	return 0;
}//Cap ou pas cap. Pas cap.

3.回文子串

最长回文子串(ural1297)

题目:

给定一个字符串,求最长回文子串。

分析:

我们考虑枚举回文子串的中心,然后其长度就是把原串反过来之后某个后缀和原串的某个后缀的最长公共前缀...然后就是RMQ问题了...

代码:

 来自机房小伙伴的嘲讽:这水题为啥要写...为啥要用后缀数组写,闲的吧...

woc...我就是想练一练后缀数组,你们至于嘛QAQ...

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=2000+5;

int n,t,be,ans,s[maxn],gs[maxn],sa[maxn],st[maxn][25],wb[maxn],wv[maxn],ran[maxn],height[maxn];

char str[maxn];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}

inline void init(void){
	for(int i=1;i<=n;i++)
		st[i][0]=height[i];
	for(int j=1;j<=20;j++)
		for(int i=1;i+(1<<j-1)<=n;i++)
			st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}

inline int query(int x,int y){
	if(y<x)	
		swap(x,y);
	x++;
	int len=y-x+1,k;
	for(k=20;k>=0;k--)
		if(len&(1<<k)||k==0)
			break;
	return min(st[x][k],st[y-(1<<k)+1][k]);
}

signed main(void){
	scanf("%s",str);t=n=strlen(str);
	for(int i=0;i<n;i++)
		s[i]=(int)str[i];
	s[n]=200;ans=1;be=0;
	for(int i=1;i<=n;i++)
		s[i+n]=s[n-i];
	n<<=1,n++;
	for(int i=0;i<n;i++)
		ran[i]=s[i];
	da(sa,ran,n+1,201);calheight(n);init();
	for(int i=1;i<t-1;i++){
		int a=query(ran[i+1],ran[t-i+1+t]);
		if(a*2+1>ans)
			ans=a*2+1,be=i-a;
		a=query(ran[i],ran[t-i+1+t]);
		if(a*2>ans)
			ans=a*2,be=i-a;
	}
	if(s[t-1]==s[t-2]&&ans<2)
		ans=2,be=t-2;
	for(int i=1;i<=ans;i++)
		printf("%c",(char)s[i+be-1]);
	puts("");
	return 0;
}//Cap ou pas cap. Pas cap.

 4.连续重复子串

连续重复子串(POJ2406)

题目:

给定一个字符串 L,已知这个字符串是由某个字符串 S 重复 R 次而得到的, 求 R 的最大值。

分析:

这题解法真多...不过本质上是一样的...

枚举循环节长度len,然后判断sunffix(1)和suffix(k+1)的最长公共前缀是否等于n-k...

但是由于数据范围有点大...倍增算法过不了...必须要用DC3...TAT...所以我就TLE到死...QAQ

代码:

先放倍增的代码...DC3的代码以后补...(怎么又挖了一个坑TAT...

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=1000000+5;

int n,s[maxn],gs[maxn],sa[maxn],wb[maxn],wv[maxn],ran[maxn],Min[maxn],height[maxn];

char str[maxn];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}

inline int solve(void){
	int i;
	for(i=1;i<=n;i++)
		if(sa[i]==0)
			break;
	if(i!=1)
		Min[i-1]=height[i];
	for(int j=i-2;j>=1;j--)
		Min[i]=min(Min[i+1],height[i]);
	if(i!=n)
		Min[i+1]=height[i+1];
	for(int j=i+1;j<=n;j++)
		Min[i]=min(Min[i-1],height[i]);
	for(i=1;i<=n;i++)
		if(n%i==0&&Min[ran[i]]==n-i)
			break;
	return n/i;
}

signed main(void){
	while(scanf("%s",str)&&str[0]!='.'){
		memset(sa,0,sizeof(sa));
		memset(wb,0,sizeof(wb));
		memset(wv,0,sizeof(wv));
		n=strlen(str);s[n]=0,ran[n]=0;
		for(int i=0;i<n;i++)
			s[i]=(int)str[i]+1,ran[i]=s[i];
		da(sa,ran,n+1,300),calheight(n);
		printf("%d
",solve());
	}
	return 0;
}//Cap ou pas cap. Pas cap.

 重复次数最多的连续重复子串(SPOJ687&POJ3693)

题目:

给定一个字符串,求重复次数最多的连续重复子串。

分析:

SPOJ的那道题比较简单。和hihocoder的解法一样...

POJ的那道题还要求字典序最小...

代码:

SPOJ687

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=100000+5;

int n,cas,s[maxn],gs[maxn],sa[maxn],wb[maxn],wv[maxn],st[maxn][25],ran[maxn],height[maxn];

char str[maxn];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}

inline void init(void){
	for(int i=1;i<=n;i++)
		st[i][0]=height[i];
	for(int j=1;j<=20;j++)
		for(int i=1;i+(1<<j-1)<=n;i++)
			st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}

inline int lcp(int x,int y){
	x=ran[x],y=ran[y];
	if(x>y)
		swap(x,y);
	x++;
	int len=y-x+1,k;
	for(k=20;k>=0;k--)
		if(len&(1<<k)||k==0)
			break;
	return min(st[x][k],st[y-(1<<k)+1][k]);
}

inline int solve(void){
	int ans=0;
	for(int len=1;len<=n;len++)
		for(int i=0;i+len<n;i+=len){
			int r=lcp(i,i+len);
			if(r/len+1>ans)
				ans=r/len;
			if(i>=len-r/len)
				if(lcp(i-len+r%len,i+r%len)/len+1>ans)
					ans=lcp(i-len+r%len,i+r%len)/len+1;
		}
	return ans; 
}

signed main(void){
	scanf("%d",&cas);
	while(cas--){
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%s",str+i);
		memset(s,0,sizeof(s));
		memset(sa,0,sizeof(sa));
		memset(wb,0,sizeof(wb));
		memset(wv,0,sizeof(wv));
		memset(ran,0,sizeof(ran));
		for(int i=0;i<n;i++)
			s[i]=(int)str[i]+1,ran[i]=s[i];
		da(sa,ran,n+1,300);calheight(n);init();
		printf("%d
",solve());
	}
	return 0;
}//Cap ou pas cap. Pas cap.

POJ3693

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=100000+5;

int n,cas,tail,s[maxn],gs[maxn],sa[maxn],wb[maxn],wv[maxn],st[maxn][25],ran[maxn],stk[maxn],height[maxn];

char str[maxn];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}

inline void init(void){
	for(int i=1;i<=n;i++)
		st[i][0]=height[i];
	for(int j=1;j<=20;j++)
		for(int i=1;i+(1<<j-1)<=n;i++)
			st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}

inline int lcp(int x,int y){
	x=ran[x],y=ran[y];
	if(x>y)
		swap(x,y);
	x++;
	int len=y-x+1,k;
	for(k=20;k>=0;k--)
		if(len&(1<<k)||k==0)
			break;
	return min(st[x][k],st[y-(1<<k)+1][k]);
}

inline void solve(void){
	int ans=0,be=-1,en;
	for(int len=1;len<=n;len++)
		for(int i=0;i+len<n;i+=len){
			int r=lcp(i,i+len);
			if(r/len+1>ans)
				ans=r/len,stk[tail=1]=len;
			else if(r/len+1==ans&&stk[tail]!=len)
				stk[++tail]=len;
			if(i>=len-r/len){
				int lala=lcp(i-len+r%len,i+r%len);
				if(lala/len+1>ans)
					ans=lala/len+1,stk[tail=1]=len;
				else if(lala/len+1==ans&&stk[tail]!=len)
					stk[++tail]=len;
			}
		}
	for(int i=1;i<=n&&be==-1;i++)
		for(int j=1;j<=tail&&be==-1;j++)
			if(lcp(sa[i],sa[i]+stk[j])/stk[j]+1==ans)
				be=sa[i],en=sa[i]+ans*stk[j];
	for(int i=be;i<en;i++)
		printf("%c",str[i]);
	puts("");
}

signed main(void){
	while(scanf("%s",str)&&str[0]!='#'){
		cas++;tail=0;
		memset(s,0,sizeof(s));
		memset(sa,0,sizeof(sa));
		memset(wb,0,sizeof(wb));
		memset(wv,0,sizeof(wv));
		memset(ran,0,sizeof(ran));
		n=strlen(str);printf("Case %d: ",cas);
		for(int i=0;i<n;i++)
			s[i]=(int)str[i]+1,ran[i]=s[i];
		da(sa,ran,n+1,300);calheight(n);init();solve();
	}
	return 0;
}//Cap ou pas cap. Pas cap.

No.3两个字符串的相关问题

1.公共子串

最长公共子串(POJ2774&ural1517)

题解

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;
//zhao kan fei niao mu fei hui,ying chuang hua luo lian chui di
 
const int maxn=200000+5;
 
int n,t,s[maxn],gs[maxn],sa[maxn],wb[maxn],wv[maxn],ran[maxn],height[maxn];
 
char s1[maxn],s2[maxn];
 
inline bool cmp(int *x,int a,int b,int l){
    return x[a]==x[b]&&x[a+l]==x[b+l];
}
 
inline void da(int *sa,int *x,int n,int m){
    int i,j,p,*y=wb;
    for(i=0;i<m;i++) gs[i]=0;
    for(i=0;i<n;i++) gs[x[i]]++;
    for(i=1;i<m;i++) gs[i]+=gs[i-1];
    for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
    for(j=1,p=1;p<n;j<<=1,m=p){
        for(i=n-j,p=0;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0;i<n;i++) wv[i]=x[y[i]];
        for(i=0;i<m;i++) gs[i]=0;
        for(i=0;i<n;i++) gs[wv[i]]++;
        for(i=1;i<m;i++) gs[i]+=gs[i-1];
        for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
        p=1;swap(x,y);x[sa[0]]=0;
        for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
    }
}
 
inline void calheight(int n){
    int i,j,k=0;
    for(i=0;i<=n;i++) ran[sa[i]]=i;
    for(i=0;i<n;height[ran[i++]]=k)
        for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}
 
inline int solve(int n){
    int ans=0;
    for(int i=1;i<=n;i++)
        if((sa[i]<t&&sa[i-1]>t)||(sa[i]>t&&sa[i-1]<t))
            ans=max(ans,height[i]);
    return ans;
}
 
signed main(void){
    scanf("%s%s",s1,s2);t=n=strlen(s1);
    for(int i=0;i<n;i++)
        s[i]=s1[i]-'a'+1;
    s[n]=100;int lala=strlen(s2);
    for(int i=1;i<=lala;i++)
        s[i+n]=s2[i-1]-'a'+1;n+=lala+1;
    for(int i=0;i<n;i++)
        ran[i]=s[i];
    da(sa,ran,n+1,101);
    calheight(n);
    printf("%d
",solve(n));
    return 0;
}//Cap ou pas cap. Pas cap.

By NeighThorn

原文地址:https://www.cnblogs.com/neighthorn/p/6291738.html