[USACO5.1] 乐曲主题Musical Themes

题目链接:戳我

Emmm......hash怎么做啊不会啊

这里是SA后缀数组版本的

就是先两两做差分,作为要处理后缀的数组。普通地求出来h数组之后,我们二分这个答案,然后判定是否合法就行了。是否合法即\(min(sa[j])+h[i]<max(sa[j]),j<=i\)

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,m,p;
int a[MAXN],b[MAXN],tp[MAXN],tax[MAXN],rnk[MAXN],sa[MAXN],h[MAXN];
inline void qsort()
{
    for(int i=1;i<=m;i++) tax[i]=0; 
    for(int i=1;i<=n;i++) tax[rnk[i]]++;
    for(int i=1;i<=m;i++) tax[i]+=tax[i-1];
    for(int i=n;i>=1;i--) sa[tax[rnk[tp[i]]]--]=tp[i];
}
inline void suffix_sort()
{
    m=MAXN,p=0;
    for(int i=1;i<=n;i++) rnk[i]=b[i],tp[i]=i;
    qsort();
    for(int w=1;p<n;m=p,w<<=1)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=n+i-w;
        for(int i=1;i<=n;i++) if(sa[i]-w>0) tp[++p]=sa[i]-w;
        qsort();
        swap(rnk,tp);
        p=rnk[sa[1]]=1;
        for(int i=2;i<=n;i++)
            rnk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])?p:++p;
    }
}
inline void get_h()
{
    int j,k=0;
    for(int i=1;i<=n;i++)
    {
        if(k) k--;
        int j=sa[rnk[i]-1];
        while(b[i+k]==b[j+k]) k++;
        h[rnk[i]]=k;
    }
    // for(int i=1;i<=n;i++) printf("rnk[%d]=%d\n",i,rnk[i]);
    // for(int i=1;i<=n;i++) printf("h[%d]=%d\n",i,h[i]);
}
inline bool check(int x)
{
    int minn=n,maxx=0;
    for(int i=2;i<=n;i++)
    {
        if(h[i]>=x) 
            minn=min(minn,min(sa[i],sa[i-1])),maxx=max(maxx,max(sa[i],sa[i-1]));
        if(h[i]<x||i==n)
        {
            if(minn+x<maxx) return true;
            minn=n,maxx=0;
        }
    }
    return false;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    n--;
    for(int i=1;i<=n;i++) b[i]=a[i+1]-a[i]+88;
    // for(int i=1;i<=n;i++) printf("b[%d]=%d\n",i,b[i]);
    suffix_sort();
    get_h();
    int l=0,r=n,ans;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        // printf("l=%d r=%d mid=%d\n",l,r,mid);
        if(check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    if(ans<4||ans>(n+1)/2) printf("0\n");
    else printf("%d\n",ans+1);
    return 0;
}
posted @ 2019-04-20 22:43  风浔凌  阅读(233)  评论(0编辑  收藏  举报