後綴自動機

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6+9;//一般有2n個等價類
int tot=1,las=1;
char s[maxn];
long long ans=0;

struct NODE
{
    int ch[26];
    int len,fa;
    NODE()
    {
        memset(ch,0,sizeof(ch));
        len=fa=0;
    }
} dian[maxn];


void add(int c)//構造自動機
{
    int p=las,np=las=++tot;
    dian[np].len=dian[p].len+1;
    for(; p&&!dian[p].ch[c]; p=dian[p].fa)
        dian[p].ch[c]=np;
    if(!p)dian[np].fa=1;
    else
    {
        int q=dian[p].ch[c];
        if(dian[q].len==dian[p].len+1)
            dian[np].fa=q;
        else
        {
            int nq=++tot;
            dian[nq]=dian[q];
            dian[nq].len=dian[p].len+1;
            dian[q].fa=dian[np].fa=nq;
            for(; p&&dian[p].ch[c]==q; p=dian[p].fa)
                dian[p].ch[c]=nq;
        }
    }
}
long long coun_dif_num()//找本質不同的子串的數量
{
    long long ans = 0;
    for(int i= 1; i<=tot; i++)
    {
        ans +=dian[i].len-(dian[dian[i].fa].len+1)+1;
        //ans += max(i)-min(i)+1;每個等價類中的字符串數量

    }
    printf("%lld\n",ans);
    return ans;

}

void all_len_max_appear()//求每個長度的子串最多的出現次數
{
    for(int i = 1; i<=tot; i++)
    {
        f[dian[i].len] = max(f[dian[i].len],r[i]);
    }
    for(int i = len; i>=1; i--)
    {
        f[i] = max(f[i],f[i+1]);
    }
    for(int i = 1; i<=len; i++)
    {
        printf("%d\n",f[i]);
       
    }
 //更新時應更新F[j]=max(F[j],|Right[i]|)j∈[minlen[i],len[i]],但其實不必每個都更新,
//因爲如果長度爲n的字符串出現了m次,那麼長度爲n-1的字符串一定至少出現了m次,及F[i]>=F[j](i<j),
//所以對於每個點只需要更新F[len[i]]=max(F[i],|Right[i]|),最後從大到小更新一下F就好了。

}

void find_min_s(int n)//求串的最小表示,原串創建自動機時要加長一倍
{
    int now,i,j;
    now = 1;
    char ans[maxn];
    for(i = 0; i <n; i++)
    {
        for(j = 0; j<26; j++)
        {
            if(dian[now].ch[j]!=0)
            {
                now = dian[now].ch[j];
                ans[i] = j+'a';
                break;
            }
        }
    }
    ans[n] = 0;
}

void Right()//求每個狀態的right集合大小
{
    for(int i = 0,p = 1; i<len; i++) //主鏈上right+1
    {
        p = dian[p].ch[s[i]-'a'];
        r[p]++;
    }

    for(int i = 1; i<=tot; i++)//基數排序
    {
        b[dian[i].len]++;
    }
    for(int i = 1; i<=len; i++)
    {
        b[i]+=b[i-1];
    }
    for(int i = 1; i<=tot; i++)
    {
        id[b[dian[i].len]--] = i;
    }

    for(int i = tot; i>=1; i--)//parent樹自地向上更新right集合大小
    {
        r[dian[id[i]].fa]+=r[id[i]];
    }
}



int max_commom(char s2[])//計算s和自動機字符串的最長公共子串
{
    int ans = 0,len = 0;
    int now = 1,len_s = strlen(s);
    for(int i = 0; i<len_s; i++)
    {
        while(now&&dian[now].ch[s[i]-'a']==0)
        {
            now = dian[now].fa;
            len = dian[now].len;
        }
        if(now)
        {
            len++;
            now = dian[now].ch[s[i]-'a'];
        }
        else
        {
            now = 1;
            len = 0;
        }
        ans = max(ans,len);
    }
    return ans;

}

void mul_max_commom(char s2[])//多個串的最長公共子串
{
    int ans = 0,len = 0;
    int now = 1,len_s = strlen(s2);
    for(int i = 0; i<len_s; i++)
    {
        while(now&&dian[now].ch[s[i]-'a']==0)
        {
            now = dian[now].fa;
            len = dian[now].len;
        }
        if(now)
        {
            len++;
            now = dian[now].ch[s[i]-'a'];
            sing_len[now] = max(sing_len[now],len);
        }
        else
        {
            now = 1;
            len = 0;
        }
    }
    for(int u = tot; u>=1; u--)
    {
        int i = id[u];
        if(dian[i].fa)
        {
            sing_len[dian[i].fa] = max(sing_len[dian[i].fa],min(sing_len[i],dian[dian[i].fa].len));
            //長度較長的子串可以匹配,那麼長度較短的子串也可以匹配

        }
        all_len[i] = min(sing_len[i],all_len[i]);
        sing_len[i] = 0;
    }
    //return max(all_len[i]);
}

int  deep(int x)//求deep,爲了求第k字典序
{
    if(dep[x]>0)
        return dep[x];
    int ans = 0;
    for(int i = 0;i < 26;i++)
    {
        if(dian[x].ch[i])
        {
            ans +=deep(dian[x].ch[i]);
        }
    }
    dep[x] = ans+1;
    return dep[x];
}

void find_min(int num)//求字典序第k小子串
{
    int now = 1,tail = -1;
    while(num>0)
    {
        int sum = 0;
        for(int i = 0;i < 26;i++)
        {
            if(!dian[now].ch[i])
                continue;
            if(sum+dep[dian[now].ch[i]]>=num)
            {
                now = dian[now].ch[i];
                s[++tail] = i+'a';
                num --;
                break;
            }
            else
            {
                sum+=dep[dian[now].ch[i]];
            }
        }
        num -= sum;
    }
    s[++tail] = 0;
}

int main()
{
    int cd;
    scanf("%s",s);
    cd=strlen(s);
    for(int i=0; i<cd; i++)
    {
        add(s[i]-'a');
    }
    coun_dif_num();

    for(int i=0; i<cd; i++)
    {
        add(s[i]-'a');
    }
   find_min_s(cd);



    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章