bzoj 2251(後綴數組/後綴自動機)

題意:

給你一個長度爲n的01串,問你這個串的全部子串中,出現次數大於1的子串的出現次數,最後按照字典序輸出。c++

分析:

對於這個題目,咱們顯然能夠用兩種處理後綴的數據結構進行處理。數組

1:後綴自動機:

我的以爲在這個題中,用後綴自動機去解決會相對來講比較好理解。數據結構

咱們知道,在後綴自動機上的結點狀態\(st\),若前一個狀態經過字符\(c\)\(st\)相連,那麼結點\(st\)表示的是\(endpos\)相同的子串的集合。而該點的\(endpos\)則表明的是以\(c\)爲結尾的串的出現的位置集合。所以咱們發現,對於一個結點\(st\),它的\(endpos\)集合的大小便是當前結點對應的子串的出現次數。那麼顯然,咱們只須要把每個狀態\(st\)\(|endpos|\)求出便可。ui

而要求\(|endpos|\),咱們只須要把\(parent\)樹反向建出來,自底向上去更新子樹的大小便是\(|endpos|\)spa

最後咱們只須要在後綴自動機上貪心的根據字典序對每個子串進行搜索便可。總的時間複雜度爲\(\mathcal{O}(n^2)\)code

2:後綴數組:

首先大概有這樣的一個性質:對於兩個不相同的後綴\(u\),\(v\),若是\(lcp(u,v)\)不爲\(0\),那麼字符串\(u\)中長度在\([1,lcp(u,v)]\)範圍內的前綴一定在\(v\)中出現過至少一次。(由於每個後綴都各不相同,而若是出現相同的前綴,那麼這個前綴一定在原串出現至少\(2\)次)排序

那麼咱們考慮將全部的後綴按照字典序進行排序。由於全部的後綴都按照字典序進行排序了,那麼若是存在一個串,知足:\(lcp(i,i-1)\le lcp(i+1,1)\),那麼$ Str[rk[i]] $ 這個串的前綴一定也在串 $ Str[rk[i+1]] $ 出現過。所以,我能夠按照字典序枚舉每個後綴 $ rk[i] $ ,並分別枚舉長度爲 $ 1 \le j \le height[i] $ 的子串,咱們發現,若是在後面的位置 $ k (i+1 \le k \le n) $ 中出現 $ height[k] \ge j $ 那麼說明長度爲 \(j\) 的子串也會在第 \(k\) 個後綴中出現,所以咱們只須要記錄第一個不知足 $ height[k] \ge j $ 的位置 \(k\) ,那麼最後的答案即爲 $ k-i+1 $ 。字符串

這樣的總體時間複雜度爲\(\mathcal{O}(n^2)\)get

代碼:

SAM:

// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define maxn 10005
using namespace std;
char str[maxn];
vector<int>res;
struct SAM{
    int next[maxn*2][2],fa[maxn*2],len[maxn*2];
    int last,cnt;
    int cntA[maxn*2],A[maxn*2];
    int num[maxn*2];
    void clear(){
        last=cnt=1;
        fa[1]=len[1]=0;
        memset(next[1],0,sizeof(next[1]));
    }
    void init(char *s){
        while(*s){
            Insert(*s-'0');
            s++;
        }
    }
    void Insert(int c){
        int p=last;
        int np=++cnt;
        memset(next[cnt],0,sizeof(next[cnt]));
        len[np]=len[p]+1;
        last=np;
        while(p&&!next[p][c]) next[p][c]=np, p=fa[p];
        if(!p) fa[np]=1;
        else{
            int q=next[p][c];
            if(len[q]==len[p]+1) fa[np]=q;
            else{
                int nq=++cnt;
                len[nq]=len[p]+1;
                memcpy(next[nq],next[q],sizeof(next[q]));
                fa[nq]=fa[q];
                fa[np]=fa[q]=nq;
                while(next[p][c]==q) next[p][c]=nq, p=fa[p];
            }
        }
    }
    void build(){
        memset(cntA,0,sizeof(cntA));
        memset(num,0,sizeof(num));
        int n=strlen(str);
        for(int i=1;i<=cnt;i++) cntA[len[i]]++;
        for(int i=1;i<=n;i++) cntA[i]+=cntA[i-1];
        for(int i=cnt;i>=1;i--) A[cntA[len[i]]--]=i;
        int tmp=1;
        for(int i=0;i<n;i++){
            num[tmp=next[tmp][str[i]-'0']]=1;
        }
        for(int i=cnt;i>=1;i--){
            int x=A[i];
            num[fa[x]]+=num[x];
        }
    }
    void dfs(int x){
        if(num[x]>1&&x!=1) res.push_back(num[x]);
        for(int i=0;i<2;i++){
            if(next[x][i]!=0)
                dfs(next[x][i]);
        }
    }
}sam;
int main()
{
    int n;
    scanf("%d%s",&n,str);
    sam.clear();
    sam.init(str);
    sam.build();
    sam.dfs(1);
    for(auto it:res){
        printf("%d\n",it);
    }
    return 0;
}

SA:

#include <bits/stdc++.h>
#define maxn 10010
using namespace std;
int n,rk[maxn],sa[maxn],height[maxn],tmp[maxn],cnt[maxn];
char str[maxn];
void SA(int n,int m){
    int i,j,k;
    n++;
    for(i=0;i<n+5;i++) rk[i]=sa[i]=height[i]=tmp[i]=0;
    for(i=0;i<m;i++) cnt[i]=0;
    for(i=0;i<n;i++) cnt[rk[i]=str[i]]++;
    for(i=1;i<m;i++) cnt[i]+=cnt[i-1];
    for(i=0;i<n;i++) sa[--cnt[rk[i]]]=i;
    for(k=1;k<=n;k<<=1){
        for(i=0;i<n;i++){
            j=sa[i]-k;
            if(j<0) j+=n;
            tmp[cnt[rk[j]]++]=j;
        }
        sa[tmp[cnt[0]=0]]=j=0;
        for(i=1;i<n;i++){
            if(rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k])
                cnt[++j]=i;
            sa[tmp[i]]=j;
        }
        memcpy(rk,sa,n*sizeof(int));
        memcpy(sa,tmp,n*sizeof(int));
        if(j>=n-1) break;
    }
    //get height[]
    i=0,k=0,height[0]=0;
    for(j=rk[0];i<n-1;i++,k++){
        while(~k&&str[i]!=str[sa[j-1]+k]){
            height[j]=k--;
            j=rk[sa[j]+1];
        }
    }
}
int main()
{
    int n;
    scanf("%d%s",&n,str);
    int len=strlen(str);
    SA(len,200);
    for(int i=2;i<=n;i++){
        for(int j=height[i-1]+1;j<=height[i];j++){
            int k=i;
            while(height[k]>=j) k++;
            if(k-i+1<=0) continue;
            printf("%d\n",k-i+1);
        }
    }
    return 0;
}
相關文章
相關標籤/搜索