ssoj 2511 后缀数组

来源:互联网 发布:ssh登录权限管理源码 编辑:程序博客网 时间:2024/06/11 10:53

离线作法,先将整个字符串读入生成一棵tire。
1.在tire上一个后缀的定义就是一个节点到末尾的路径。
2.那么接下来考虑如何维护这些后缀的sa,和rank。
3.会发现一个后缀在树上是一段连续的“树枝”,不会拐弯,这个性质非常的有用。由此,我们倍增计算sa和rank时只需像倍增求lca一样往上跳2的j次方个节点就行了。
4.接下来是lcp的问题,在这里我们可以不需要求出height数组,只需要将每一次倍增计算出的rank值保存下来.
rank[i][j]表示以i开头的后缀在第j次排序中的排名,即在tire上向上跳2^j步后,以那个节点为开头的后缀的排名,如果排名相同,就是一个前缀。

#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>#include<cstring>#include<string>#include<cmath>#include<set>#define LL long longusing namespace std;const int maxn=200063;char ch[maxn];int r[maxn],len,las,cnt,logs=18;int wa[maxn],wb[maxn],wv[maxn],wr[maxn],sa[maxn],num[maxn];int d[maxn],f[maxn][25],rank[maxn][25];LL ans[maxn];struct Node{    int x;    bool operator<(const Node &rhs)const{        return rank[x][logs]<rank[rhs.x][logs];    }};set<Node> s;bool cmp(int *r,int a,int b,int l){    return r[a]==r[b]&&r[f[a][l]]==r[f[b][l]];}void build_sa(int n,int m){    int *x=wa,*y=wb,*t;    for(int i=1;i<=m;i++) wr[i]=0;    for(int i=1;i<=n;i++) wr[x[i]=r[i]-96]++;    for(int i=2;i<=m;i++) wr[i]+=wr[i-1];    for(int i=n;i>=1;i--) sa[wr[i]--]=i;    for(int i=1;i<=n;i++) rank[i][0]=x[i];    for(int j=0,p;j<logs;j++,m=p){        for(int i=0;i<=m;i++) wr[i]=0;        for(int i=1;i<=n;i++){            if(!f[i][j]) wr[wv[i]=0]++;            else wr[wv[i]=x[f[i][j]]]++,f[i][j+1]=f[f[i][j]][j];        }        for(int i=1;i<=m;i++) wr[i]+=wr[i-1];        for(int i=n;i>=1;i--) y[wr[wv[i]]--]=i;        for(int i=1;i<=n;i++) wv[i]=x[y[i]];//用第二关键字排序后的结果         for(int i=0;i<=m;i++) wr[i]=0;        for(int i=1;i<=n;i++) wr[wv[i]]++;        for(int i=2;i<=m;i++) wr[i]+=wr[i-1];        for(int i=n;i>=1;i--) sa[wr[wv[i]]--]=y[i];        t=x;x=y;y=t; rank[sa[1]][j+1]=x[sa[1]]=1;p=1;        for(int i=2;i<=n;i++){            rank[sa[i]][j+1]=x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p:++p;         }     }}int lcp(int a,int b){    int res=0;    for(int i=logs;i>=0;i--){        if(rank[a][i]==rank[b][i])            res+=(1<<i),a=f[a][i],b=f[b][i];        }    return res;}int main(){    scanf(" %s",ch+1);    len=strlen(ch+1);   las=0;    for(int i=1;i<=len;i++){        if(isalpha(ch[i])){            f[++cnt][0]=las;d[cnt]=d[las]+1;            las=cnt;r[las]=ch[i];        }        else las=f[las][0];        num[i]=las;      }    build_sa(cnt,30);    for(int i=1;i<=len;i++){        int now=num[i];        if(isalpha(ch[i])){            ans[now]=ans[f[now][0]]+d[now];            //cout<<ans[now]<<endl;            set<Node>::iterator nxt=s.lower_bound((Node){now}),pre;            if(nxt!=s.end()) ans[now]-=lcp(now,nxt->x);            if(nxt!=s.begin()){                pre=nxt;pre--;                ans[now]-=lcp(now,pre->x);                if(nxt!=s.end()) ans[now]+=lcp(nxt->x,pre->x);            }            s.insert((Node){now});        }        else s.erase((Node){num[i-1]});        printf("%lld\n",ans[now]);    }    return 0;}
0 0