1. 程式人生 > >poj2774(字尾陣列||字串hash)

poj2774(字尾陣列||字串hash)

求兩個串的最長公共子串

將兩個串連起來,求字尾陣列,sa中相鄰兩個字尾如果不屬於同一個模版,則用這個height更新答案,他們的最長公共字首更新答案

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<set>
#define max(x,y) (x)>(y)?(x):(y)
using namespace std;
const int N=1000005;
int n,s[N],c[N],t1[N],t2[N],sa[N],height[N],rank[N],len1,len2;
char s1[N],s2[N];
void build_sa()
{
	int m=30,*x=t1,*y=t2;
	
	for (int i=0;i<m;i++) c[i]=0;
	for (int i=0;i<n;i++) c[x[i]=s[i]]++;
	for (int i=1;i<m;i++) c[i]+=c[i-1];
	for (int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
	
	for (int k=1;k<=n;k<<=1)
	{
		int p=0;
		for (int i=n-k;i<n;i++) y[p++]=i;
		for (int i=0;i<n;i++) if (sa[i]>=k) y[p++]=sa[i]-k;
		
		for (int i=0;i<m;i++) c[i]=0;
		for (int i=0;i<n;i++) c[x[y[i]]]++;
		for (int i=1;i<m;i++) c[i]+=c[i-1];
		for (int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
		swap(x,y);
		x[sa[0]]=0;p=1;
		for (int i=1;i<n;i++)
		x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
		if (p>n) break;
		m=p; 
	}
}
void build_height()
{
	int j,k=0;
	for (int i=0;i<n;i++) rank[sa[i]]=i;
	for (int i=0;i<n-1;i++)
	{
		if (k) k--;
		j=sa[rank[i]-1];
		while (s[j+k]==s[i+k]) k++;
		height[rank[i]]=k;
	}
}
int main()
{
	scanf("%s",s1);
	scanf("%s",s2);
	len1=strlen(s1),len2=strlen(s2);
	for (int i=0;i<len1;i++) s[n++]=s1[i]-'a';
	s[n++]=28;
	for (int i=0;i<len2;i++) s[n++]=s2[i]-'a';
	s[n++]=0;
	build_sa();
	build_height();
	int ans=0;
	for (int i=2;i<n-1;i++)
	if ((sa[i]<len1&&sa[i-1]>len1)||(sa[i]>len1&&sa[i-1]<len1))
	ans=max(ans,height[i]);
 	printf("%d\n",ans);
	return 0;
}

單hash+setTLE

自然溢位+set也是TLE了,

更新2016.8.8(改為排序後二分查詢)

KO!果然set就是慢,把第一組的hash值存到陣列中,排序,第二組時用lower_bound二分判斷是否出現,不過,lower_bound是找大於等於的第一個數,

不一定是等於的,所以在求出下標之後,再判斷一下是否等於,如果等於才能返回true

這題各種不過。。

反思:

一:求一個子串的hash值,在預處理字首hash時,hs【0】要設為0,因為,第一個數是字串開頭!!

二:如果是判斷另一組資料中有沒有和一組資料中相同的;set太慢!!!!能不用就不用,可以存在一個數組中,sort後二分查詢判斷有無.快很多

三:lower_bound是返回大於等於的第一個數,不一定是等於,如果要找等於,那麼還要再判斷一下返回的下標(減陣列頭指標)所指的數是否等於這個數

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
using namespace  std;
typedef unsigned long long ll;
const int N=500009;
const int p=29;
inline int idx(char ch){return ch-'a';}
/********************************************/
char s1[N],s2[N];
int len1,len2;
ll hs1[N],hs2[N],po[N];
ll a[N];
bool pan(int mid)
{
	for (int i=1;i<=len1-mid+1;i++)
	a[i]=hs1[i+mid-1]-hs1[i-1]*po[mid];
	int n=len1-mid+1;
	sort(a+1,a+n+1); //排序
	int t;
	for (int i=1;i<=len2-mid+1;i++)
	{
		t=lower_bound(a+1,a+n+1,hs2[i+mid-1]-hs2[i-1]*po[mid])-a;//二分位置
	 	if (a[t]==hs2[i+mid-1]-hs2[i-1]*po[mid]) return true; //判斷是否相同,是否出現
	}
	return false;
}
int main()
{
	scanf("%s",s1+1);
	scanf("%s",s2+1);
	len1=strlen(s1+1),len2=strlen(s2+1);
	po[0]=1;
	for (int i=1;i<N;i++) po[i]=po[i-1]*p; 
	for (int i=1;i<=len1;i++) hs1[i]=hs1[i-1]*p+idx(s1[i]);
	for (int i=1;i<=len2;i++) hs2[i]=hs2[i-1]*p+idx(s2[i]);
	int ans=0,l=1,r=min(len1,len2),mid;
	while (l<=r)
	{
		mid=(l+r)>>1;
		if (pan(mid)) ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d",ans);
	return 0;
}