2946: [Poi2000]公共串|哈希|后缀数组

据说是后缀自动机裸题

然而,我还没有熟练掌握CE自动机,TLE自动机,RE自动机

并没有达到学习后缀自动机条件…………

后缀数组 :
二分答案,给height数组分组暴力判断

复杂度(nlogn)36ms

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define T 11111
using namespace std;
char s[T];
int t1[T],t2[T],rank[T],cc[T],sa[T],height[T];
int n[6],st[9]={0,-1};
int len,m,L,R=T,ans;
bool cmp(int *y,int a,int b,int k)
{
	int a1=y[a],b1=y[b];
	int a2=a+k>=len?-1:y[a+k];
	int b2=b+k>=len?-1:y[b+k];
	return a1==b1&&a2==b2;
}
void make_sa()
{
	int *x=t1,*y=t2,m=333;
	for(int i=0;i<len;i++)++cc[x[i]=s[i]];
	for(int i=1;i<m;i++)cc[i]+=cc[i-1];
	for(int i=len-1;~i;i--)sa[--cc[x[i]]]=i;
	for(int k=1;k<len;k<<=1)
	{
		int p=0;
		for(int i=len-k;i<len;i++)y[p++]=i;
		for(int i=0;i<len;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
		for(int i=0;i<m;i++)cc[i]=0;
		for(int i=0;i<len;i++)++cc[x[y[i]]];
		for(int i=1;i<m;i++)cc[i]+=cc[i-1];
		for(int i=len-1;~i;i--)sa[--cc[x[y[i]]]]=y[i];
		swap(x,y),m=1,x[sa[0]]=0;
		for(int i=1;i<len;i++)x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?m-1:m++;
		if(m>len)return;
	}
}
void make_height()
{
	int k=0;
	for(int i=0;i<len;i++)rank[sa[i]]=i;
	for(int i=0;i<len;i++)
	{
		if(!rank[i])continue;
		int j=sa[rank[i]-1];
		if(k)k--;
		while(s[i+k]==s[j+k])k++;
		height[rank[i]]=k;
	}
}	
int search(int x)
{
	if(s[x]=='#')return 0;
	for(int i=2;i<=m;i++)
		if(x<st[i])return i-1;
	return m;
}	
bool jud(int x)
{
	bool q[6];
	int now=1,i;
	while(now<len)
	{
		if(height[now]>=x)
		{
			memset(q,0,sizeof(q));
			q[search(sa[now-1])]=1;
			while(height[now]>=x&&now<len)
			{
				q[search(sa[now])]=1;
				now++;
			}
			for(i=1;i<=m;i++)if(!q[i])break;
			if(i>m)return 1;
		}
		else now++;
	}
	return 0;
}
int main()
{
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%s",s+len);
		len+=(n[i]=strlen(s+len));
		s[len]='#';
		len++;
		st[i+1]=len-1;
		R=min(R,n[i]);
	}
	make_sa();
	make_height();
	while(L<=R)
	{
		int mid=L+R>>1;
		if(jud(mid))ans=mid,L=mid+1;
		else R=mid-1;
	}
	cout<<ans;
	return 0;
}

Hash:

字符串哈希 

二分答案 

求长度为当前答案长度的hash值存在hash表里

排序二分查找暴力判断

复杂度(nlog^2n)40ms

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define ll unsigned long long
#define T 2111
using namespace std;
char s[5][T];
ll P[T],f[5][T],h[T],hash[T];
ll Base=131;
int top,n,L,R=T,ans,cnt;
int l[5],mark[T];
int find(ll  x)
{
	int l=1,r=cnt;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(hash[mid]==x)return mid;
		else if(hash[mid]>x)r=mid-1;
		else if(hash[mid]<x)l=mid+1;
	}
	return 0;
}
bool jud(int x)
{
	cnt=top=0;
	for(int i=0;i+x<=l[0];i++)
	{
		h[++top]=f[0][i+x]-f[0][i]*P[x];
	}
	sort(h+1,h+top+1);
	for(int i=1;i<=top;i++)
	    if(h[i]!=h[i-1])
	    {
	    	hash[++cnt]=h[i];
	    }
	memset(mark,0,sizeof(mark));
	for(int i=1;i<n;i++)
	{
		for(int j=0;j+x<=l[i];j++)
		{
			ll p=f[i][j+x]-f[i][j]*P[x];
			int x=find(p);
			if(x!=0&&mark[x]==i-1)mark[x]=i;
		}
	}
	for(int i=1;i<=cnt;i++) 
	    if(mark[i]==n-1)return 1;
	return 0;
}	
int main()
{
	P[0]=1; for(int i=1;i<=2000;i++)P[i]=P[i-1]*Base;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%s",s[i]+1);
		l[i]=strlen(s[i]+1);
		f[i][0]=0;
		for(int j=1;j<=l[i];j++)
		{
			f[i][j]=f[i][j-1]*Base+s[i][j];
		}
		R=min(l[i],R);
	}
	while(L<=R)
	{
		int mid=L+R>>1;
		if(jud(mid))ans=mid,L=mid+1;
		else R=mid-1;
	}
	cout<<ans;
	return 0;
}



你可能感兴趣的:(hash,后缀数组)