[联合集训6-12] String 后缀树

先考虑假设我们把所有后缀建成一个Trie树,那么一个后缀字典序能成为最大就会带来一些限制,具体地就是从根出发遍历这个串,遍历的边上的字母映射的值要大于其他边上的字母。我们只要对于每个后缀带来的限制判一下环即可,复杂度 O(n2c) O ( n 2 c ) ,其中 c c 为字符集大小。
可以利用后缀树的性质优化上面的操作,也就是把只有一条出边的点压缩起来,其中第一条边的字母充当压缩后的边上的字母即可。因为SAM的节点数是O(n)的,所以复杂度降到 O(nc2) O ( n c 2 )

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define C 12
using namespace std;
char s[N];
int tote,n,mp[C][C],ans[N],con[N<<1],to[N<<1],nxt[N<<1],w[N<<1],tim;
bool vis[C],insta[C];
void ins(int x,int y,int z)
{
    to[++tote]=y;
    w[tote]=z;
    nxt[tote]=con[x];
    con[x]=tote;
}
struct sam
{
    int last,cnt,fa[N<<1],a[N<<1][C],mx[N<<1],pos[N<<1];
    void clr()
    {
        for(int i=1;i<=cnt;i++)
        {
            fa[i]=mx[i]=pos[i]=0;   
            for(int c=0;c<C;c++)
                a[i][c]=0;
        }
        last=cnt=1;
    }
    void extend(int c,int x)
    {
        int p=last,np=++cnt;last=np;
        mx[np]=mx[p]+1;pos[np]=x;
        for(;p&&!a[p][c];p=fa[p]) a[p][c]=np;
        if(!p) fa[np]=1;
        else
        {
            int q=a[p][c];
            if(mx[q]==mx[p]+1) fa[np]=q;
            else
            {
                int nq=++cnt;mx[nq]=mx[p]+1;pos[nq]=pos[q];
                for(int ic=0;ic<C;ic++)
                    a[nq][ic]=a[q][ic];
                fa[nq]=fa[q];
                fa[q]=fa[np]=nq;
                for(;p&&a[p][c]==q;p=fa[p]) a[p][c]=nq;             
            }
        }
    }
}T;
bool dfs(int v)
{
    vis[v]=1;insta[v]=1;
    bool re=0;
    for(int c=0;c<C&&!re;c++)
        if(mp[v][c])
        {
            if(insta[c]) {return 1;}
            if(!vis[c]) re=re|dfs(c);
        }
    insta[v]=0; 
    return re;  
}

void walk(int x)
{
    bool fff=0;
    for(int p=con[x];p;p=nxt[p])
    {
        for(int q=con[x];q;q=nxt[q])
            if(w[p]!=w[q]) mp[w[p]][w[q]]++;
        walk(to[p]);
        for(int q=con[x];q;q=nxt[q])
            if(w[p]!=w[q]) mp[w[p]][w[q]]--;
        fff=1;
    }
    if(!fff)
    {
        int rd=0;bool flag=1;
        memset(vis,0,sizeof(vis));
        memset(insta,0,sizeof(insta));
        tim=0;
        for(int c=0;c<C;c++)
            if(!vis[c])
                if(dfs(c)) {flag=0;break;}
        ans[n-T.pos[x]+1]=flag;
    }   
}
int main()
{
    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        for(int i=1;i<=n;i++)
            ans[i]=0;
        for(int i=1;i<=T.cnt;i++)
            con[i]=0;
        for(int i=1;i<=tote;i++)
            to[i]=nxt[i]=0;
        tote=0;             
        T.clr();
        scanf("%s",s+1);
        n=strlen(s+1);
        reverse(s+1,s+n+1);
        for(int i=1;i<=n;i++)
            T.extend(s[i]-'a',i);

        for(int i=2;i<=T.cnt;i++)
            ins(T.fa[i],i,s[T.pos[i]-T.mx[T.fa[i]]]-'a');

        walk(1);
        for(int i=1;i<=n;i++)
            putchar('0'+ans[i]);
        puts("");   

    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值