HDU5769后缀数组,高度数组模板

来源:互联网 发布:陆逊 知乎 编辑:程序博客网 时间:2024/06/11 03:43

首先学一波字符串的一些性质。首先求出字符串的后缀数组和高度数组之后,就可以求出这个字符串有多少个不同的字串。



PS:这个后缀数组模板为倍增+基数排序。复杂度nlog(n),基数排序O(n),倍增是log但是常数太大,.高度数组可以利用后缀数组O(n)求出!

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <iostream>#include <algorithm>using namespace std;typedef long long ll;const int inf=0x3f3f3f3f;const int MAXN=100010;int sa[MAXN];int t1[MAXN],t2[MAXN],c[MAXN];int Rank[MAXN],lcp[MAXN];void construct_sa(char s[],int n,int m){    int i,j,p,*x=t1,*y=t2;    for(i=0;i<m;i++)c[i]=0;    for(i=0;i<n;i++)c[x[i]=s[i]]++;    for(i=1;i<m;i++)c[i]+=c[i-1];    for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;    for(j=1;j<=n;j<<=1){        p=0;        for(i=n-j;i<n;i++)y[p++]=i;        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;        for(i=0;i<m;i++)c[i]=0;        for(i=0;i<n;i++)c[x[y[i]]]++;        for(i=1;i<m;i++)c[i]+=c[i-1];        for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];        swap(x,y);        p=1;x[sa[0]]=0;        for(i=1;i<n;i++)            x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;        if(p>=n)break;        m=p;    }}void construct_lcp(char s[],int n){    int h=0;lcp[0]=0;    for(int i=0;i<=n;i++) Rank[sa[i]]=i;    for(int i=0;i<n;i++){        if(h>0)h--;        int j=sa[Rank[i]-1];        for(;j+h<n&&i+h<n;h++)        {            if(s[j+h]!=s[i+h])                break;        }        lcp[Rank[i]]=h;    }}int fvis[MAXN],pr[MAXN];char str[MAXN];int main(){    int T,cas=1;    char ch[10];    scanf("%d",&T);    while(T--){        scanf("%s",ch);        scanf("%s",str);        int n=strlen(str),flag=0,fpr;        memset(fvis,0,sizeof(fvis));        memset(pr,0,sizeof(pr));        for(int i=n-1;i>=0;i--){            if(str[i]==ch[0]){                flag=1;fpr=i;            }            if(flag) fvis[i]=1,pr[i]=fpr;        }        ll ans=0;        construct_sa(str,n+1,128);        construct_lcp(str,n);        for(int i=0;i<=n;i++){            if(fvis[sa[i]]) ans=ans+n-max((sa[i]+lcp[i]),pr[sa[i]]);        }        printf("Case #%d: %I64d\n",cas++,ans);    }    return 0;}

下面这个是nlog(n)log(n)的模板,做这个题就超时了,利用的是倍增的思想,但是是普通的排序,(nlog(n)).

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <iostream>#include <algorithm>using namespace std;typedef long long ll;const int inf=0x3f3f3f3f;const int MAXN=100010;int sa[MAXN];int nn;int kk;int Rank[MAXN],lcp[MAXN],tmp[MAXN];bool compare_sa(int i,int j){    if(Rank[i]!=Rank[j])        return Rank[i]<Rank[j];    else    {        int ri=i+kk<=nn?Rank[i+kk]:-1;        int rj=j+kk<=nn?Rank[j+kk]:-1;        return ri<rj;    }}void construct_sa(char s[],int n){    for(int i=0;i<=n;i++)    {        sa[i]=i;        Rank[i]=i<n?sa[i]:-1;    }    for(kk=1;kk<=n;kk*=2)        sort(sa,sa+n+1,compare_sa);    tmp[sa[0]]=0;    for(int i=1;i<=n;i++)    {        tmp[sa[i]]=tmp[sa[i-1]]+(compare_sa(sa[i-1],sa[i])?1:0);    }    for(int i=0;i<=n;i++)    {        Rank[i]=tmp[i];    }}void construct_lcp(char s[],int n){    int h=0;lcp[0]=0;    for(int i=0;i<=n;i++) Rank[sa[i]]=i;    for(int i=0;i<n;i++){        if(h>0)h--;        int j=sa[Rank[i]-1];        for(;j+h<n&&i+h<n;h++)        {            if(s[j+h]!=s[i+h])                break;        }        lcp[Rank[i]]=h;    }}int fvis[MAXN],pr[MAXN];char str[MAXN];int main(){    int T,cas=1;    char ch[10];    scanf("%d",&T);    while(T--){        scanf("%s",ch);        scanf("%s",str);        int n=strlen(str),flag=0,fpr;        memset(fvis,0,sizeof(fvis));        memset(pr,0,sizeof(pr));        for(int i=n-1;i>=0;i--){            if(str[i]==ch[0]){                flag=1;fpr=i;            }            if(flag) fvis[i]=1,pr[i]=fpr;        }        ll ans=0;        nn=n+1;        construct_sa(str,n+1);        construct_lcp(str,n);        for(int i=0;i<=n;i++){            if(fvis[sa[i]]) ans=ans+n-max((sa[i]+lcp[i]),pr[sa[i]]);        }        printf("Case #%d: %I64d\n",cas++,ans);    }    return 0;}


原创粉丝点击