BZOJ4516: [Sdoi2016]生成魔咒-後綴數組+線段樹+RMQ

傳送門

題意:

給出一個字符串,分別求出前1~n位所含的不同的字符串個數

n<=100000

Solution:

看這道題的時候感覺到有後綴排序的那麼點意思,然而只是感覺到而已…

正解太TM神了

考慮對於一個字符串,他所產生的本質不同的字符串個數爲所有後綴的長度和減去height數組的和

那麼我們如何動態的求呢?我們每次是在字符串後面增加一個字符,這樣所有的後綴都會改變,非常不好維護,所以我們就翻轉整個字符串,每次在字符串前面加字符,這樣舊的後綴就不會改變,只會增加一個新的後綴

那麼我們的問題就變成了:增加一個字符串,求他與前面的所有字符串中最長的LCP是多少

考慮先求出整個字符串的後綴數組,那麼我們每次加入一個字符串,求得的最長的LCP一定是在這個字符串前後與它rank最近的字符串中產生,求這個字符串的的前後最近可以用平衡樹或線段樹來維護, 求LCP可以通過height數組RMQ求出

總複雜度O(nlogn)

代碼:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200010;
int a[N],n,p;
int lsh[N],cnt;
int SA[N],rk[N],tp[N],m,tax[N],h[N],f[N][20],lg[N],mi[20];
long long ans;
struct tree{
    int l,r,maxn,minn;
}tr[4*N];
void Jsort()
{
    for (int i=1;i<=m;i++) tax[i]=0;
    for (int i=1;i<=n;i++) tax[rk[tp[i]]]++;
    for (int i=2;i<=m;i++) tax[i]+=tax[i-1];
    for (int i=n;i>=1;i--) SA[tax[rk[tp[i]]]--]=tp[i];
}
bool cmp(int tp[],int x,int y,int w)
{
    return (tp[x]==tp[y]&&tp[x+w]==tp[y+w]);
}
int queryh(int l,int r)
{
    int len=r-l+1;
    return min(f[l][lg[len]],f[r-mi[lg[len]]+1][lg[len]]);
}
void build(int i,int l,int r)
{
    tr[i].l=l;tr[i].r=r;tr[i].minn=1e9;
    if (l==r) return;
    int mid=l+r>>1; 
    build(i<<1,l,mid);build(i<<1|1,mid+1,r);
}
void modify(int i,int pos,int x)
{
    int L=tr[i].l,R=tr[i].r;
    if (L==R) {tr[i].maxn=x;tr[i].minn=x;return;}
    int mid=L+R>>1;
    if (x<=mid) modify(i<<1,pos,x);
    else modify(i<<1|1,pos,x);
    tr[i].maxn=max(tr[i<<1].maxn,tr[i<<1|1].maxn);
    tr[i].minn=min(tr[i<<1].minn==0?1e9:tr[i<<1].minn,tr[i<<1|1].minn==0?1e9:tr[i<<1|1].minn);
}
int querymax(int i,int l,int r)
{
    if (l>r) return 0;
    int L=tr[i].l,R=tr[i].r;
    if (L>r||l>R) return 0;
    if (l<=L&&R<=r) return tr[i].maxn;
    return max(querymax(i<<1,l,r),querymax(i<<1|1,l,r));
}
int querymin(int i,int l,int r)
{
    if (l>r) return 0;
    int L=tr[i].l,R=tr[i].r;
    if (L>r||l>R) return 1e9;
    if (l<=L&&R<=r) return tr[i].minn;
    return min(querymin(i<<1,l,r),querymin(i<<1|1,l,r));
}
int main()
{
    scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&a[i]),lsh[++cnt]=a[i];
    sort(lsh+1,lsh+1+cnt);cnt=unique(lsh+1,lsh+1+cnt)-lsh-1;
    for (int i=1;i<=n;i++) a[i]=lower_bound(lsh+1,lsh+1+cnt,a[i])-lsh;
    int l=1,r=n;
    for (;l<r;l++,r--) swap(a[l],a[r]);
    m=cnt;
    for (int i=1;i<=n;i++) tp[i]=i,rk[i]=a[i];
    Jsort();
    for (int w=1;p<n;w+=w,m=p)
    {
        p=0;
        for (int i=n-w+1;i<=n;i++) tp[++p]=i;
        for (int i=1;i<=n;i++) if (SA[i]>w) tp[++p]=SA[i]-w;
        Jsort();swap(tp,rk);
        p=1;rk[SA[1]]=p;
        for (int i=2;i<=n;i++) if (cmp(tp,SA[i],SA[i-1],w)) rk[SA[i]]=p;else rk[SA[i]]=++p;
    }
    int k=0;
    build(1,1,n);
    for (int i=1;i<=n;i++)
    {
        if (rk[i]==1) {h[1]=0;continue;} 
        if (k) k--;
        int j=SA[rk[i]-1];
        while (a[i+k]==a[j+k]&&i+k<=n&&j+k<=n) k++;
        h[rk[i]]=k;
    }
    mi[0]=1;lg[1]=0;for (int i=1;mi[i-1]<=n;i++) mi[i]=mi[i-1]*2,lg[mi[i]]=i;
    for (int i=2;i<=n;i++) if (!lg[i]) lg[i]=lg[i-1];
    for (int i=1;i<=n;i++) f[i][0]=h[i];
    for (int i=1;i<=19;i++)
        for (int j=1;j<=n;j++)
            f[j][i]=min(f[j][i-1],f[min(n,j+(1<<i-1))][i-1]);
    for (int i=n;i>=1;i--)
    {
        int pre=querymax(1,1,rk[i]-1),nxt=querymin(1,rk[i]+1,n);
        int tmp=0;
        if (pre) tmp=max(queryh(pre+1,rk[i]),tmp);
        if (nxt<=n) tmp=max(queryh(rk[i]+1,nxt),tmp);
        ans+=n-i+1-tmp;
        modify(1,rk[i],rk[i]);
        printf("%lld\n",ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章