poj2774(字尾陣列||字串hash)
阿新 • • 發佈:2019-01-03
求兩個串的最長公共子串
將兩個串連起來,求字尾陣列,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; }