ACdream 1430 SETI 后缀自动机/后缀数组 不重叠子串的个数

来源:互联网 发布:如何将ubuntu安装到u盘 编辑:程序博客网 时间:2024/06/10 16:41

题目求不重叠子串的个数。

一开始看错题第一反应就是用SAM来解,求出right集,如果出现次数超过1次就统计。

不过本题要求的是不重叠的子串,因此在求right集时,顺便维护两个值,每个结点所能表示的所有原串的终点的最小值和最大值。

最后遍历所有结点统计答案。O(n)

如果用SA来做的话就是枚举所有的子串长度k,对于每个k,对heght进行分组,对每个分组,求出sa最小值和最大值,最大值减去最小值是否不小于k,如果是就把答案加一。

枚举O(n),统计O(n),总复杂度O(n^2),由于时限是10s所以没超时。


/** this code is made by cyendra* Problem: 1430* Verdict: Accepted* Submission Date: 2014-10-06 18:07:26* Time: 96MS* Memory: 14864KB*/#include <iostream>#include <cstring>#include <cstdio> using namespace std;typedef long long LL;const int INF=0x3f3f3f3f;const int maxn=30000;const int maxm=13000;/***************    SAM 真·模板***************/struct State {    State *par;    State *go[52];    int val; // max,当前状态能接收的串的最长长度    int mi;    int mx;    int right; // right集,表示当前状态可以在多少个位置上出现    void init(int _val = 0){        par = 0;        val = _val;        mx=-1;        mi=INF;        right=0;        memset(go,0,sizeof(go));    }    int calc(){ // 表示该状态能表示多少中不同的串        if (par==0) return 0;        return val-par->val;    }};State *root, *last, *cur;State nodePool[maxn];State* newState(int val = 0) {    cur->init(val);    return cur++;}//int total; // 不同的子串个数。void initSAM() {    //total = 0;    cur = nodePool;    root = newState();    last = root;}void extend(int w,int pos) {    State* p = last;    State* np = newState(p->val + 1);    np->right=1; // 设置right集    np->mi=np->mx=pos;    while (p && p->go[w] == 0) {        p->go[w] = np;        p = p->par;    }    if (p == 0) {        np->par = root;        //total+=np->calc();    }    else {        State* q = p->go[w];        if (p->val + 1 == q->val) {            np->par = q;            //total+=np->calc();        }        else {            State* nq = newState(p->val + 1);            memcpy(nq->go, q->go, sizeof(q->go));            //total -= q->calc();            nq->par = q->par;            q->par = nq;            np->par = nq;            //total += q->calc()+nq->calc()+np->calc();            while (p && p->go[w] == q) {                p->go[w] = nq;                p = p->par;            }        }    }    last = np;} int d[maxm];State* b[maxn];void topo(){ // 求出parent树的拓扑序    int cnt=cur-nodePool;    int maxVal=0;    memset(d,0,sizeof(d));    for (int i=1;i<cnt;i++) maxVal=max(maxVal,nodePool[i].val),d[nodePool[i].val]++;    for (int i=1;i<=maxVal;i++) d[i]+=d[i-1];    for (int i=1;i<cnt;i++) b[d[nodePool[i].val]--]=&nodePool[i];    b[0]=root;} void gaoSamInit(){ // 求出SAM的附加信息    State* p;    int cnt=cur-nodePool;    for (int i=cnt-1;i>0;i--){        p=b[i];        p->par->right+=p->right;        p->par->mi=min(p->par->mi,p->mi);        p->par->mx=max(p->par->mx,p->mx);    }}  char s[maxm]; int main(){    scanf("%s",s);    initSAM();    for (int i=0;s[i];i++){        extend(s[i]-'a',i);    }    topo();    gaoSamInit();    int ans=0;    State* p;    int cnt=cur-nodePool;    for (int i=cnt-1;i>0;i--){        p=b[i];        if (p->right>1) {            int lim=p->mx-p->mi;            int L=p->par->val+1;            int R=p->val;            if (R<=lim) ans+=p->calc();            else if (L>lim) continue;            else if (lim>=L&&lim<R) ans+=lim-L+1;        }    }    printf("%d\n",ans);    return 0;}


--------

/** this code is made by cyendra* Problem: 1430* Verdict: Accepted* Submission Date: 2014-10-06 13:10:01* Time: 5740MS* Memory: 3804KB*/#include <iostream>#include <cstring>#include <cstdio> using namespace std;typedef long long LL;const int maxn=20000;const int maxm=13000;const int INF=0x3f3f3f3f;/********************************************************************  后缀数组 Suffix Array**  INIT:solver.call_fun(char* s);**  CALL: solver.lcp(int i,int j); //后缀i与后缀j的最长公共前缀**  SP_USE: solver.LCS(char *s1,char* s2); //最长公共字串******************************************************************/struct SuffixArray{    int r[maxn];    int sa[maxn],rank[maxn],height[maxn];    int t[maxn],t2[maxn],c[maxn],n;    int m;//模板长度    void init(char* s){        n=strlen(s);        for (int i=0;i<n;i++) r[i]=int(s[i]);        m=300;    }    int cmp(int *r,int a,int b,int l){        return r[a]==r[b]&&r[a+l]==r[b+l];    }    /**    字符要先转化为正整数    待排序的字符串放在r[]数组中,从r[0]到r[n-1],长度为n,且最大值小于m。    所有的r[i]都大于0,r[n]无意义算法中置0    函数结束后,结果放在sa[]数组中(名次从1..n),从sa[1]到sa[n]。s[0]无意义    **/    void build_sa(){        int i,k,p,*x=t,*y=t2;        r[n++]=0;        for (i=0;i<m;i++) c[i]=0;        for (i=0;i<n;i++) c[x[i]=r[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 (k=1,p=1;k<n;k*=2,m=p){            for (p=0,i=n-k;i<n;i++) y[p++]=i;            for (i=0;i<n;i++) if (sa[i]>=k) y[p++]=sa[i]-k;            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]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;        }        n--;    }    /**    height[2..n]:height[i]保存的是lcp(sa[i],sa[i-1])    rank[0..n-1]:rank[i]保存的是原串中suffix[i]的名次    **/    void getHeight(){        int i,j,k=0;        for (i=1;i<=n;i++) rank[sa[i]]=i;        for (i=0;i<n;i++){            if (k) k--;            j=sa[rank[i]-1];            while (r[i+k]==r[j+k]) k++;            height[rank[i]]=k;        }    }    int d[maxn][20];    //元素从1编号到n    void RMQ_init(int A[],int n){        for (int i=1;i<=n;i++) d[i][0]=A[i];        for (int j=1;(1<<j)<=n;j++)            for (int i=1;i+j-1<=n;i++)                d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);    }    int RMQ(int L,int R){        int k=0;        while ((1<<(k+1))<=R-L+1) k++;        return min(d[L][k],d[R-(1<<k)+1][k]);    }    void LCP_init(){        RMQ_init(height,n);    }    int lcp(int i,int j){        if (rank[i]>rank[j]) swap(i,j);        return RMQ(rank[i]+1,rank[j]);    }    void call_fun(char* s){        init(s);//初始化后缀数组        build_sa();//构造后缀数组sa        getHeight();//计算height与rank        LCP_init();//初始化RMQ    }    int LCS(char* s1,char* s2){        int p,ans;        int l=strlen(s1);        p=l;        s1[l]='$';        s1[l+1]='\0';        strcat(s1,s2);        call_fun(s1);        ans=0;        for (int i=2;i<=n;i++)            if ((sa[i-1]<p&&sa[i]>p)||(sa[i-1]>p&&sa[i]<p)) ans=max(ans,height[i]);        return ans;    }    int gao(int k,int n){        int maxx=0,minn=INF,ans=0;        for (int i=2;i<=n;i++){            if (height[i]<k){                if (maxx-minn>=k){                    ans++;                }                maxx=0,minn=INF;            }            else{                maxx=max(maxx,max(sa[i-1],sa[i]));                minn=min(minn,min(sa[i-1],sa[i]));            }        }        if (maxx-minn>=k) ans++;        return ans;    }}solver; char s[maxn];int main(){    //freopen("seti.in","r",stdin);    //freopen("seti.out","w",stdout);    scanf("%s",s);    //gets(s);    int len=strlen(s);    solver.init(s);    solver.build_sa();    solver.getHeight();    int ans=0;    for (int i=1;i<=len/2;i++){        ans+=solver.gao(i,len);    }    printf("%d\n",ans);    return 0;}




0 0