1. 程式人生 > >【POJ2774】Long Long Message(字尾陣列求Height陣列)

【POJ2774】Long Long Message(字尾陣列求Height陣列)

點此看題面

大致題意: 求兩個字串中最長公共子串的長度。


關於字尾陣列

關於\(Height\)陣列的概念以及如何用字尾陣列求\(Height\)陣列詳見這篇部落格:字尾陣列入門(二)——Height陣列與LCP


大致思路

由於字尾陣列是處理一個字串的,因此我們第一步自然是將這兩個字串拼在一起,並在中間加一個不可能出現的字元,例如\(\%\)

然後我們用字尾陣列求出其\(Height\)陣列。

注意一個性質,答案肯定是按字典序排名後相鄰字尾的\(LCP\)值中的最大值

因此,我們只要列舉\(i\),判斷字尾\(_{SA_i}\)與字尾\(_{SA_{i-1}}\)

的起始字元是否在同一個字串內,然後更新答案即可。

回顧\(Height\)陣列定義就是\(Height_i=LCP(i,i-1)\)

因此我們其實不用再額外去求相鄰字尾的\(LCP\),直接呼叫\(Height\)陣列即可。

具體實現詳見程式碼。


程式碼

#include<cstdio>
#include<cstring>
#define N 100000
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,m;char s[(N<<1)+5];
class Class_SuffixArray//字尾陣列
{
    private:
        int n,rk[(N<<1)+5],pos[(N<<1)+5],tot[(N<<1)+5];
        inline void RadixSort(int S)
        {
            register int i;
            for(i=0;i<=S;++i) tot[i]=0;
            for(i=1;i<=n;++i) ++tot[rk[i]];
            for(i=1;i<=S;++i) tot[i]+=tot[i-1];
            for(i=n;i;--i) SA[tot[rk[pos[i]]]--]=pos[i];
        }
        inline void GetSA(char *s)
        {
            register int i,k,cnt=0,Size=122;
            for(n=strlen(s),i=1;i<=n;++i) rk[pos[i]=i]=s[i-1];
            for(RadixSort(Size),k=1;cnt<n;k<<=1)
            {
                for(Size=cnt,cnt=0,i=1;i<=k;++i) pos[++cnt]=n-k+i;
                for(i=1;i<=n;++i) SA[i]>k&&(pos[++cnt]=SA[i]-k);
                for(RadixSort(Size),i=1;i<=n;++i) pos[i]=rk[i];
                for(rk[SA[1]]=cnt=1,i=2;i<=n;++i) rk[SA[i]]=(pos[SA[i-1]]^pos[SA[i]]||pos[SA[i-1]+k]^pos[SA[i]+k])?++cnt:cnt;
            }
        }
        inline void GetHeight(char *s)
        {
            register int i,j,k=0;
            for(i=1;i<=n;++i) rk[SA[i]]=i;
            for(i=1;i<=n;++i)
            {
                if(!(rk[i]^1)) continue;
                k&&--k,j=SA[rk[i]-1];
                while(i+k<=n&&j+k<=n&&!(s[i+k-1]^s[j+k-1])) ++k;
                Height[rk[i]]=k;
            }
        }
    public:
        int SA[(N<<1)+5],Height[(N<<1)+5];
        inline void Init(char *s) {GetSA(s),GetHeight(s);}
}S;
int main()
{
    register int i,ans=0;
    scanf("%s",s),n=strlen(s),s[n]='%',scanf("%s",s+n+1),m=strlen(s+n+1);
    for(S.Init(s),i=1;i<=n+m;++i) if((S.SA[i]<n)^(S.SA[i-1]<n)) Gmax(ans,S.Height[i]);//如果相鄰兩個字尾的起始字元不在同一個字串內,就更新答案
    return printf("%d",ans),0;
}