【字符串】后缀自动机SAM

后缀自动机 SAM

模板

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+50;

struct Node{
    int ch[26];
    int len,fa;
}node[maxn<<1];
int las=1,tot=1;
/*
结点node[]表示一个类
las是未加入此字符前的最长前缀(==整个串)所属的结点编号
tot是当前结点的总数
*/
int siz[maxn<<1];
ll num;//num 存不同子串的数量
ll alllen;//所有不同子串的总长度
void add(int c)
{
    int p=las;int np=las=++tot;siz[tot]=1;
    node[np].len=node[p].len+1;
    for(;p&&!node[p].ch[c];p=node[p].fa)node[p].ch[c]=np;
    //p=fa(p) 即从长到短遍历旧串的所有后缀
    //node[p].ch[c]==0 如果旧串后缀添加新的字符c不是旧串字串 则 往新串的最长后缀所属结点连一条c边
    if(!p)node[np].fa=1;//字符c在旧串没有出现过 所以新类的父类只能是1
    else
    {
        int q=node[p].ch[c];
        if(node[q].len==node[p].len+1)node[np].fa=q;//q是找到的第一个与np不同而又有后缀关系的结点
        else//node[q].len>node[p].len+1
        {
            int nq=++tot;
            node[nq]=node[q];//strcpy(node[nq].ch,node[q].ch);ndoe[nq].fa=node[q].fa;
            node[nq].len=node[p].len+1;
            node[q].fa=node[np].fa=nq;
            for(;p&&node[p].ch[c]==q;p=node[p].fa)node[p].ch[c]=nq;
        }
    }
    num+=node[np].len-node[node[np].fa].len;
    alllen+=1ll*node[np].len*(node[np].len+1)/2-1ll*(node[node[np].fa].len)*(node[node[np].fa].len+1)/2;
}
char s[maxn];int len;

//求出现次数最多的子串的次数********************************************
//begin1
int mx;//mx是子串出现次数的最大次数
vector<int>p[maxn<<1];
void dfsinit(){
    for(int i=2;i<=tot;i++)
        p[node[i].fa].push_back(i);
}
void dfs(int u)
{
    for(int i=0;i<p[u].size();i++)
    {
        dfs(p[u][i]);
        siz[u]+=siz[p[u][i]];
    }
    if(siz[u]!=1)mx=max(mx,siz[u]);
}
void solve()
{
    dfsinit();
    dfs(1);
    printf("%d\n",mx);
}
//end1

//输出第k小字符串*********************************************************
//begin2
/*
 * 对于以下两种情况,在跑dfs2之前 让siz[1]=0 (siz[root]=0)
 * 若是求可重复的第k小的字符串,则在dfs2()的函数里,让siz[u]=1,不用跑dfs
 * 若是求本质不同第k小的字符串, 则先跑上边的dfsinit()和dfs() ,然后用下边的dfs2
 */
int siz2[maxn];bool vis[maxn];
void dfs2(int u)
{
    siz2[u]=siz[u];vis[u]=true;
    for(int i=0;i<26;i++)
        if(node[u].ch[i])
        {
            if(!vis[node[u].ch[i]])dfs2(node[u].ch[i]);
            siz2[u]+=siz2[node[u].ch[i]];
        }
}
void getch(int u,int num)
{
    num-=siz[u];
    if(num<=0)return;
    for(int i=0;i<26;i++)
    {
        if(num<=siz2[node[u].ch[i]])
        {
            putchar(i+'a');
            getch(node[u].ch[i],num);
            return;
        }
        num-=siz2[node[u].ch[i]];
    }
}
//end2


//求本质不同第k小字符串 的bfs做法***********************************************
//begin3
int bb[maxn],id[maxn],sz[maxn];
void bfs(){
    for(int i=1;i<=tot;i++)bb[node[i].len]++;
    for(int i=1;i<=tot;i++)bb[i]+=bb[i-1];
    for(int i=1;i<=tot;i++)id[bb[node[i].len]--]=i;
    for(int i=tot;i>=1;i--){
        sz[id[i]]=1;
        for(int j=0;j<26;j++)
            if(node[id[i]].ch[j])sz[id[i]]+=sz[node[id[i]].ch[j]];
    }
}
void query(int k)
{
    int u=1;
    while(k)
    {
        for(int i=0;i<26;i++)
        {
            if(!node[u].ch[i])continue;
            if(k>sz[node[u].ch[i]])k-=sz[node[u].ch[i]];
            else{
                putchar('a'+i);
                u=node[u].ch[i];
                k--;break;
            }
        }
    }
    puts("");
}
//end3



int main()
{
    scanf("%s",s);len=strlen(s);
    for(int i=0;i<len;i++)add(s[i]-'a');
    int op,k;
    scanf("%d%d",&op,&k);
    //求第k小字符串
    if(op){
        dfsinit();
        dfs(1);
    }
    else{
        for(int i=2;i<=tot;i++)siz[i]=1;
    }
    siz[1]=0;
    dfs2(1);
    if(k>siz2[1]){
        puts("-1");return 0;
    }
    getch(1,k);
}
posted @ 2020-10-11 11:04  草丛怪  阅读(139)  评论(0编辑  收藏  举报