1
8
2016
0

【poj3415】Common Substrings

题意:给你两个字符串和k,求不小于k的公共子串的个数。

计算A的某个后缀与B的某个后缀的最长公共前缀L,如果L大于等于k,就加上L-k+1。

我们先把B复制到A后面,并加一个分隔符。

我们考虑同一组大于等于k的height,对于每个B计算之前每个A对其的贡献,对于每个A计算之前每个B对其的贡献。但这样是N2的。所以,我们利用lcp的性质,i~j所有height的最小值,所以我们只要维护一个单调递增的栈即可。

代码:

 

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int K,l,n,top;
ll sum,ans;
int wa[200100],wb[200100],ws[200100],wv[200100],sa[200100],rank[200100],r[200100],height[200100],f[200100],a[200100],b[200100];
char s[200100];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for (i=0;i<m;i++) ws[i]=0;
	for (i=0;i<n;i++) ws[x[i]=r[i]]++;
	for (i=1;i<m;i++) ws[i]+=ws[i-1];
	for (i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for (j=1,p=1;p<n;j*=2,m=p)
	{
		p=0;
		for (i=n-j;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++) ws[i]=0;
		for (i=0;i<n;i++) ws[wv[i]]++;
		for (i=1;i<m;i++) ws[i]+=ws[i-1];
		for (i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for (i=1;i<=n;i++) rank[sa[i]]=i;
	for (i=0;i<n;height[rank[i++]]=k)
		for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int main()
{
	while (1)
	{
		scanf("%d",&K);
		if (!K) break;
		scanf("%s",s);
		l=strlen(s);
		s[l]=1;;
		scanf("%s",s+l+1);
		n=strlen(s);
		for (int i=0;i<n;i++) r[i]=s[i];
		r[n]=0;
		da(r,sa,n+1,200);
		calheight(r,sa,n);
		for (int i=2;i<=n;i++)
		{
			f[i]=(sa[i]<l);
			height[i]-=K-1;
			if (height[i]<0) height[i]=0;
		}
		height[n+1]=0;
		a[0]=-1;ans=0;
		for (int id=0;id<=1;id++)
		{
			sum=0,top=0;
			for (int i=2;i<=n;i++)
			{
				if (f[i]!=id) ans+=sum;
				a[++top]=height[i+1];
				b[top]=(f[i]==id);
				sum+=(ll)a[top]*b[top];
				while (a[top-1]>=a[top])
				{
					sum-=(ll)(a[top-1]-a[top])*b[top-1];
					a[top-1]=a[top];
					b[top-1]+=b[top];
					top--;
				}
			}
		}
		printf("%lld\n",ans);
	}
}
Category: 后缀数组 | Tags:
1
8
2016
0

【poj1226】Substrings

题意:给你n个串,找到最长长度的字符串,使得其或其的回文串,是每个串的子串,输出长度。

把所有n个串和其回文串都连起来。然后二分答案,对于每个大于等于mid的每组height,只要n个字符串都存在就可行。

代码:

#include<cstdio>
#include<cstring>
using namespace std;
int T,n,len,l;
int wa[100100],wb[100100],ws[100100],wv[100100],sa[100100],r[100100],rank[100100],height[100100],flag[110],id[100100];
char s[1010];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for (i=0;i<m;i++) ws[i]=0;
	for (i=0;i<n;i++) ws[x[i]=r[i]]++;
	for (i=1;i<m;i++) ws[i]+=ws[i-1];
	for (i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for (j=1,p=1;p<n;j*=2,m=p)
	{
		p=0;
		for (i=n-j;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++) ws[i]=0;
		for (i=0;i<n;i++) ws[wv[i]]++;
		for (i=1;i<m;i++) ws[i]+=ws[i-1];
		for (i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for (i=1;i<=n;i++) rank[sa[i]]=i;
	for (i=0;i<n;height[rank[i++]]=k)
		for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int check(int mid)
{
	int j;
	for (int i=1;i<=len;i=j+1)
	{
		for (;height[i]<mid&&i<=len;i++);
		for (j=i;height[j]>=mid;j++);
		if (j-i+1<n) continue;
		memset(flag,0,sizeof(flag));
		int tot=0;
		for (int k=i-1;k<j;k++)
			if (!flag[id[sa[k]]]){flag[id[sa[k]]]=1;tot++;}
		if (tot>=n) return 1;
	}
	return 0;
}
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		len=0;
		for (int i=1;i<=n;i++)
		{
			scanf("%s",s);
			l=strlen(s);
			for (int j=0;j<l;j++) {r[len+j]=s[j]+200;id[len+j]=i;}
			r[len+l]=i*2-1;
			id[len+l]=0;
			len+=l+1;
			for (int j=0;j<l;j++) {r[len+j]=s[l-1-j]+200;id[len+j]=i;}
			r[len+l]=i*2;
			id[len+l]=0;
			len+=l+1;
		}
		len--;r[len]=0;
		da(r,sa,len+1,400);
		calheight(r,sa,len);
		height[len+1]=-1;
		int L=0,R=100;
		while (L<R)
		{
			int mid=((L+R)>>1)+1;
			if (check(mid)) L=mid;else R=mid-1;
		}
		if (n==1) printf("%d\n",len/2);else printf("%d\n",R);
	}
}
Category: 后缀数组 | Tags:
1
7
2016
0

【poj3294】Life Forms

题意:给你n个字符串,求最长的字符串使得它是至少n/2+1个字符串的子串,输出所有这样的字符串。

首先我们把n个字符串全部连起来,中间用分隔符连起来。二分答案,然后对于每一组大于等于mid的height,只要判断是否存在于大于n/2个字符串中。

代码:

 

#include<cstdio>
#include<cstring>
using namespace std;
int n,len,l,num;
int wa[100100],wb[100100],ws[100100],wv[100100],sa[100100],r[100100],rank[100100],height[100100],flag[110],id[100100],ans[100100];
char s[1010];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} 
void da(int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for (i=0;i<m;i++) ws[i]=0;
	for (i=0;i<n;i++) ws[x[i]=r[i]]++;
	for (i=1;i<m;i++) ws[i]+=ws[i-1];
	for (i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for (j=1,p=1;p<n;j*=2,m=p)
	{
		p=0;
		for (i=n-j;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++) ws[i]=0;
		for (i=0;i<n;i++) ws[wv[i]]++;
		for (i=1;i<m;i++) ws[i]+=ws[i-1];
		for (i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for (i=1;i<=n;i++) rank[sa[i]]=i;
	for (i=0;i<n;height[rank[i++]]=k)
		for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int check(int mid)
{
	int isok=0,j;
	for (int i=1;i<=len;i=j+1)
	{
		for (;height[i]<mid&&i<=len;i++);
		for (j=i;height[j]>=mid;j++)
		if (j-i+1<n/2+1) continue;
		memset(flag,0,sizeof(flag));
		int tot=0;
		for (int k=i-1;k<j;k++)
			if (!flag[id[sa[k]]]){flag[id[sa[k]]]=1;tot++;}
		if (tot>=n/2+1)
		{
			if (!isok){isok=1;num=0;}
			ans[++num]=sa[i];
		}
	}
	return isok;
}
int main()
{
	scanf("%d",&n);
	while (n)
	{
		len=0;num=0;
		for (int i=1;i<=n;i++)
		{
			scanf("%s",s);
			l=strlen(s);
			for (int j=0;j<l;j++) {r[len+j]=s[j]+100;id[len+j]=i;}
			r[len+l]=i;
			id[len+l]=0;
			len+=l+1;
		}
		len--;r[len]=0;
		da(r,sa,len+1,280);
		calheight(r,sa,len);
		height[len+1]=-1;
		int L=0,R=1000;
		while (L<R)
		{
			int mid=((L+R)>>1)+1;
			if (check(mid)) L=mid;else R=mid-1;
		}
		if (!R)printf("?\n");else
		{
			for (int i=1;i<=num;i++)
			{
				int k=ans[i];
				for (int j=0;j<R;j++) printf("%c",r[k+j]-100);
				printf("\n");
			}
		}
		scanf("%d",&n);
		if (n) printf("\n");
	}
}
Category: 后缀数组 | Tags:
1
7
2016
0

【poj1743】 Musical Theme

题意:给你一个数字串,找到最长长度且不重叠的两个子串,使得这两个子串变化相同。

什么叫变化相同呢,就是比如{1,3,4,6,9}与{5,7,8,10,13}就是变化相同的。

首先我们可以把数字串的相邻两个相减,得到的新序列。我们只要在这个新序列中找到最长的相等的且不重叠的两个子串。我们可以先二分答案,然后由于lcp的性质,height可以分为若干组,每组中的height都是大于等于mid的,只要同一组存在两个数使得它们在原串的的位置差大于mid,就可行。

代码:

 

#include<cstdio>
#include<algorithm>
using namespace std;
int n,x,y;
int wa[20010],wb[20010],ws[20010],wv[20010],r[20010],sa[20010],rank[20010],height[20010];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for (i=0;i<m;i++) ws[i]=0;
	for (i=0;i<n;i++) ws[x[i]=r[i]]++;
	for (i=1;i<m;i++) ws[i]+=ws[i-1];
	for (i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for (j=1,p=1;p<n;j*=2,m=p)
	{
		p=0;
		for (i=n-j;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++) ws[i]=0;
		for (i=0;i<n;i++) ws[wv[i]]++;
		for (i=1;i<m;i++) ws[i]+=ws[i-1];
		for (i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for (i=1;i<=n;i++) rank[sa[i]]=i;
	for (i=0;i<n;height[rank[i++]]=k)
		for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int check(int *sa,int n,int mid)
{
	int Min=sa[1],Max=sa[1];
	for (int i=2;i<=n;i++)
	{
		if (height[i]<mid) Min=Max=sa[i];else
		{
			Max=max(Max,sa[i]);
			Min=min(Min,sa[i]);
			if (Max-Min>mid) return 1;
		}
	}
	return 0;
}
int main()
{
	while (1)
	{
		scanf("%d",&n);
		if (!n) break;
		scanf("%d",&y);
		n--;
		for (int i=0;i<n;i++)
		{
			scanf("%d",&x);
			r[i]=x-y+100;
			y=x;
		}
		r[n]=0;
		da(r,sa,n+1,200);
		calheight(r,sa,n);
		int l=1,r=n/2;
		while (l<r)
		{
			int mid=((l+r)>>1)+1;
			if (check(sa,n,mid)) l=mid;else r=mid-1;
		}
		if (r>=4) printf("%d\n",r+1);else printf("0\n");
	}
}
Category: 后缀数组 | Tags:
1
7
2016
0

【poj2774】Long Long Message

本题是翻开后缀数组专题的第一题。

题意:给你两个字符串A,B,求两个字符串的最长公共子串。

求两个字符串的最长公共子串也就是求两个字符串的两个后缀的最长公共前缀的最大值。

那么我们只要将B复制要A后面,中间加个分隔符。求出sa,rank,height三个数组。之后,我们只要求sa[i-1],sa[i]在不同字符串中且height[i]最大的height值。

代码:

 

#include<cstdio>
#include<cstring>
using namespace std;
int l,n,ans;
char s[200010];
int sa[200010],r[200010],rank[200010],height[200010],wa[200010],wb[200010],ws[200010],wv[200010];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for (i=0;i<m;i++) ws[i]=0;
	for (i=0;i<n;i++) ws[x[i]=r[i]]++;
	for (i=1;i<m;i++) ws[i]+=ws[i-1];
	for (i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for (j=1,p=1;p<n;j*=2,m=p)
	{
		for (p=0,i=n-j;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++) ws[i]=0;
		for (i=0;i<n;i++) ws[wv[i]]++;
		for (i=1;i<m;i++) ws[i]+=ws[i-1];
		for (i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for (i=1;i<=n;i++) rank[sa[i]]=i;
	for (i=0;i<n;height[rank[i++]]=k)
	for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int main()
{
	scanf("%s",s);
	l=strlen(s);
	s[l]=1;
	scanf("%s",s+l+1);
	n=strlen(s);
	for (int i=0;i<n;i++) r[i]=s[i];
	r[n]=0;
	da(r,sa,n+1,128);
	calheight(r,sa,n);
	for (int i=2;i<=n;i++)
	if (height[i]>ans)
	if ((l<sa[i-1]&&l>sa[i])||(l>sa[i-1]&&l<sa[i])) ans=height[i];
	printf("%d\n",ans);
}
Category: 后缀数组 | Tags:

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com