zoukankan      html  css  js  c++  java
  • bzoj千题计划306:bzoj2342: [Shoi2011]双倍回文 (回文自动机)

    https://www.lydsy.com/JudgeOnline/problem.php?id=2342

    解法一:

    对原串构建回文自动机

    抽离fail树,从根开始dfs

    设len[x]表示节点x表示的最长回文子串长度

    在fail树上,x到根节点的路径上的点表示的字符串包含了x代表的回文子串的所有回文后缀/前缀

    所以若dfs到了x,若len[x]为偶数,标记len[x]*2,如果在x的子树中能找到len为len[x]*2的点,那么len[x]*2*2就可以用来更新答案

     
    #include<cstdio>
    #include<algorithm>
     
    using namespace std;
     
    #define N 500001
     
    char ss[N+1];
    int s[N+1];
     
    int tot=1,last;
    int len[N],fail[N],tr[N][26];
    int p,c,np,t;
     
    bool ok[N<<1];
    int ans;
     
    int front[N],to[N],nxt[N],cnt;
     
    void add(int u,int v)
    {
        to[++cnt]=v; nxt[cnt]=front[u]; front[u]=cnt;
    }
     
    void extend(int i)
    {
        p=last; c=s[i];
        while(s[i-1-len[p]]!=c) p=fail[p];
        if(!tr[p][c])
        {
            np=++tot;
            len[np]=len[p]+2;
            t=fail[p];
            while(s[i-1-len[t]]!=c) t=fail[t];
            fail[np]=tr[t][c];
            add(fail[np],np);
            tr[p][c]=np;
        }
        else np=tr[p][c];
        last=np;
    }
     
    void dfs(int x)
    {
        if(ok[len[x]]) ans=max(ans,len[x]);
        if(!(len[x]&1)) ok[len[x]<<1]=true;
        for(int i=front[x];i;i=nxt[i]) dfs(to[i]);
        if(!(len[x]&1)) ok[len[x]<<1]=false;
    }
     
    int main()
    {
        int n;
        scanf("%d",&n);
        scanf("%s",ss+1);
        s[0]=-1;
        for(int i=1;i<=n;++i) s[i]=ss[i]-'a';
        fail[0]=1;
        len[1]=-1;
        for(int i=1;i<=n;++i) extend(i);
        dfs(0);
        printf("%d",ans);
    }
    View Code

    解法二:

    原串和其反串拼接,中间用两个不一样的字符隔开

    然后构建回文自动机

    考虑一个双倍回文的分割点i和i+1

    i是前缀回文的结束位置

    i+1是后缀回文的开始位置

    设以i为结束位置的最长回文子串为s1,在回文自动机上的节点为a

    设以i+1开始位置的最长回文子串为s2,在回文自动机上的节点为b

    设前缀以i结束,后缀以i+1开始的双倍回文子串的一半为s,长度为L

    那么现在有两个要求:

    1、L为偶数

    2、s是s1的后缀,s是s2的前缀,且s最长

    对于要求2,因为开始原串和反串拼接构建了回文自动机,所以就是求a和b在fail树上的LCA

    对于要求1,对每个点x记录fail树上 x的祖先中离它最近的长度为偶数的回文串即可

    倍增求LCA会超时

    不会tarjan求LCA(~~~~(>_<)~~~~)

    树链剖分求LCA 会被卡空间

    最后还是选了树剖。。。

    回文自动机用了map存储,

    注意回文自动机中有节点0,在树剖第二遍dfs的时候,重儿子初始化的编号不能是0

     

    #include<map>
    #include<cmath>
    #include<cstdio>
    #include<algorithm>
     
    using namespace std;
     
    #define N 1000001
     
    int n,m;
    char ss[N+1];
    int s[N+1];
     
    int tot=1,last;
    int len[N],fail[N];
    map<int,int>tr[N];
    int p,c,np,t;
     
    int use[N],pos[N];
     
    int front[N],to[N],nxt[N],cnt;
     
    int bl[N],dep[N],siz[N],fa[N];
     
    void add(int u,int v)
    {
        to[++cnt]=v; nxt[cnt]=front[u]; front[u]=cnt;
    }
     
    void extend(int i)
    {
        p=last; c=s[i];
        while(s[i-1-len[p]]!=c) p=fail[p];
        if(!tr[p][c])
        {
            np=++tot;
            len[np]=len[p]+2;
            t=fail[p];
            while(s[i-1-len[t]]!=c) t=fail[t];
            fail[np]=tr[t][c];
            add(fail[np],np);
            use[np]=len[np]&1 ? use[fail[np]] : np;
            tr[p][c]=np;
        }
        else np=tr[p][c];
        last=np;
        pos[i]=np;
    }
     
    void build()
    {
        s[0]=-1;
        for(int i=1;i<=n;++i) s[i]=ss[i]-'a';
        m=n;
        s[++m]=26; s[++m]=27;
        for(int i=n;i;--i) s[++m]=ss[i]-'a';
        fail[0]=1;
        len[1]=-1;
        for(int i=1;i<=m;++i) extend(i);
    }
     
    void dfs1(int x)
    {
        siz[x]=1;
        for(int i=front[x];i;i=nxt[i])
        {
            dep[to[i]]=dep[x]+1;
            fa[to[i]]=x;
            dfs1(to[i]);
            siz[x]+=siz[to[i]];
        }
    }
     
    void dfs2(int x,int top)
    {
        int y=-1;
        bl[x]=top;
        for(int i=front[x];i;i=nxt[i])
            if(y==-1 || siz[to[i]]>siz[y]) y=to[i];
        if(y==-1) return;
        dfs2(y,top);
        for(int i=front[x];i;i=nxt[i])
            if(to[i]!=y) dfs2(to[i],to[i]);
    }
     
    int get_lca(int u,int v)
    {
        while(bl[u]!=bl[v])
        {
            if(dep[bl[u]]<dep[bl[v]]) swap(u,v);
            u=fa[bl[u]];
        }
        return dep[u]<dep[v] ? u : v;
    }
     
    void solve()
    {
        add(1,0);
        dfs1(1);
        dfs2(1,1);
        int ans=0;
        int lca;
        for(int i=1;i<=n;++i)
        {
            lca=get_lca(pos[i],pos[m-i]);
        //  printf("%d %d
    ",pos[i],pos[m-i]);
            ans=max(ans,len[use[lca]]<<1);
            //printf("%d
    ",ans);
        }
        printf("%d",ans);
    }
     
    int main()
    {
        scanf("%d",&n);
        scanf("%s",ss+1);
        build();
        solve();
    }
    View Code
  • 相关阅读:
    67 个拯救前端开发者的工具、库和资源
    js常用的工具函数
    npm快捷键
    给bootstrap table设置行列单元格样式
    script标签中type为"text/x-template"或"text/html"
    【转载】Ogre3d 2.1 源码编译安装教程
    【转载】OGRE 2.1 Windows 编译
    【转载】DXUT11框架浅析(4)--调试相关
    【转载】GitHub详解
    【转载】3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8687833.html
Copyright © 2011-2022 走看看