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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章