POJ1743 Musical Theme (後綴數組,不可重疊最長重複子串)

POJ1743 Musical Theme (後綴數組,不可重疊最長重複子串)

題目鏈接:傳送門

思路:

​ 首先可以知道對於兩個相同旋律的拍子(即兩個拍子全部增加某個值後會相同),假設長度爲k,那麼拍子的後k-1個數與他們的前一個數的差都是相同的。

​ 所以我們可以將數組轉化爲差數組,d[1]=d[1]=無效值,然後要求從差數組中找到長度最長的兩個子序列滿足,子序列不重疊且間隔爲至少1(如果這兩個子序列挨着的話,那麼對應的拍子就重疊了)

​ 所以我們可以二分這個子序列的長度,對於每個長度,我們根據height[]height[]是否小於長度來進行分組,對於每個分組,如果這個組的下標最大值與最小值滿足maxmin>=len+1max-min>=len+1​,則這個解可以。

#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<string.h>
#define mset(a,b)   memset(a,b,sizeof(a))
typedef long long ll;
const int N=2e4+10;
int t1[N],t2[N],c1[N];//輔助數組
void SA(int *s,int n,int sa[],int rank[],int height[])//基數排序的版本中
{
    int m=200;//桶的大小,會在下面循環中變化,第一關鍵詞r的最大值,初始時是字符的最大值,之後都<=n
    int *sb=t1,*r=t2,*c=c1;//輔助數組,分別爲:基數排序所用的第二2,rak',cnt數組
    //用基數排序求出長度爲1的sa[]和rank[],如果字符最大值較大,第一輪可以採用sort
    for(int i=0; i<=m; ++i) c[i]=0;
    for(int i=1; i<=n; ++i) c[r[i]=s[i]]++;
    for(int i=1; i<=m; ++i) c[i]+=c[i-1];
    for(int i=n; i>=1; --i) sa[c[s[i]]--]=i;

    for(int k=1,p ; k < n; k<<=1 ) //p是一個計數器,現在還沒用。
    {
        //sb[i]:第二關鍵詞排名爲i的位置爲sb[i]
        p=0;
        for(int i=n-k+1; i<=n; ++i) sb[++p]=i;
        for(int i=1; i<=n; ++i) if(sa[i]>k) sb[++p]=sa[i]-k;
        //基數排序求出2k長度的sa數組
        for(int i=0; i<=m; ++i) c[i]=0;
        for(int i=1; i<=n; ++i) c[r[i]]++;
        for(int i=1; i<=m; ++i) c[i]+=c[i-1];
        for(int i=n; i>=1; --i) sa[ c[r[sb[i]]]-- ]=sb[i];
        std::swap(r,sb);
        //現在要利用上輪的r和這輪的sa求這輪的r
        r[sa[1]]=p=1;
        for(int i=2,a,b; i<=n; ++i)
        {
            a=sa[i],b=sa[i-1];
            if(sb[a]==sb[b]&&(a+k<=n && b+k<=n&&sb[a+k]==sb[b+k]) ) r[a]=p;
            else r[a]=++p;
        }
        if(p>=n) break;//可以提前退出
        m=p;
    }
    /*計算高度數組*/
    int k=0;
    for(int i=1; i<=n; ++i) rank[sa[i]]=i;
    height[1]=0;
    for(int i=1; i<=n; ++i)
    {
        if(k) k--;
        int j=sa[rank[i]-1];
        if(j==0) continue;
        while(s[j+k]==s[i+k]) ++k;
        height[rank[i]]=k;
    }
}
int rank[N],sa[N],height[N]; //height[1]的值爲0
int w[N],d[N];
/*
搞成差分數組 前綴相同長度至少爲4的不重疊
*/
bool judge(int v,int n)//在v數組中能否構成長度>=v的不重疊子串
{
    //分成幾個不相交的區間,求每個區間的
    int l=1,minn=sa[1],maxx=sa[1];
    for(int i=2;i<=n;++i)
    {
        if(height[i]>=v) {
            minn=std::min(minn,sa[i]);
            maxx=std::max(maxx,sa[i]);
        }
        else{
            if(maxx-minn>=v) return true;
            else minn=sa[i],maxx=sa[i];
        }
    }
    if(maxx-minn>=v+1) return true;
    else return false;
}
int main()
{
    int n;
    while(scanf("%d",&n),n)
    {
        for(int i=1;i<=n;++i) scanf("%d",w+i),d[i]=w[i]-w[i-1]+100;
        d[1]=0;
        d[n+1]=-1;
        SA(d,n,sa,rank,height);
        int l=4,r=n;
        int ans=-1;
        while(l<=r)
        {
            int m=l+r>>1;
            if(judge(m,n))
            {
                ans=m;
                l=m+1;
            }
            else r=m-1;
        }
        if(ans==-1)
            printf("0\n");
        else
            printf("%d\n",ans+1);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章