zoukankan      html  css  js  c++  java
  • 回文自动机pam

    目的:类似回文Trie树+ac自动机,可以用来统计一些其他的回文串相关的量

    复杂度:O(nlogn)

    https://blog.csdn.net/Lolierl/article/details/99971257

    https://www.luogu.org/problem/P5496

    求出以每个位置结尾的回文子串个数,强制在线

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=2e6+10;
    struct pam_trie
    {
        int ch[26];
        int fail,len,num;
    };
    struct pam
    {
        pam_trie b[maxn];
        int n,length,last,cnt,s[maxn];
        char c[maxn];
        pam()
        {
            b[0].len=0;b[1].len=-1;
            b[0].fail=1;b[1].fail=0;
            last=0;
            cnt=1;
        }
        void read()
        {
            scanf("%s",c+1);
            length=strlen(c+1);
        }
        int get_fail(int x)
        {
            while(s[n-b[x].len-1]!=s[n])x=b[x].fail;
            return x;
        }
        void insert()
        {
            int p=get_fail(last); 
            if(!b[p].ch[s[n]])
            {
                b[++cnt].len=b[p].len+2;
                b[cnt].fail=b[get_fail(b[p].fail)].ch[s[n]];
                //b[cnt].num=b[b[cnt].fail].num+1;
                b[p].ch[s[n]]=cnt;
            }
            last=b[p].ch[s[n]];
            b[last].num=b[b[last].fail].num+1;
        }
        void solve()
        {
            int k=0;
            s[0]=26;
            for(n=1;n<=length;n++)
            {
                c[n]=(c[n]-97+k)%26+97;
                s[n]=c[n]-'a';
                insert();
                printf("%d ",b[last].num);
                k=b[last].num;
            }
        }
    }P;
    
    int main()
    {
        P.read();
        P.solve();
        return 0;
    }
    View Code

    https://www.luogu.org/problem/P3649

    求回文子串出现次数*长度的最大值

    #include<iostream>
    #include<stdio.h>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=3e5+10;
    struct pam_trie
    {
        int ch[26];
        int fail,len,sum;
    };
    struct pam
    {
        pam_trie b[maxn];
        int n,length,last,cnt,s[maxn];
        char c[maxn];
        long long ans;
        pam()
        {
            b[0].len=0;b[1].len=-1;
            b[0].fail=1;b[1].fail=0;
            last=0;
            cnt=1;
        }
        void read()
        {
            scanf("%s",c+1);
            length=strlen(c+1);
        }
        int get_fail(int x)
        {
            while(s[n-b[x].len-1]!=s[n])x=b[x].fail;
            return x;
        }
        void insert()
        {
            int p=get_fail(last);
            if(!b[p].ch[s[n]])
            {
                b[++cnt].len=b[p].len+2;
                b[cnt].fail=b[get_fail(b[p].fail)].ch[s[n]];
                b[p].ch[s[n]]=cnt;
            }
            last=b[p].ch[s[n]];
            b[last].sum++;
        }
        void solve()
        {
            s[0]=26;
            for(n=1;n<=length;n++)
            {
                s[n]=c[n]-'a';
                insert();
            }
            ans=0;
            for(int i=cnt;i>0;i--)
            {
                b[b[i].fail].sum+=b[i].sum;
                ans=max(ans,1ll*b[i].sum*b[i].len);
            }
            printf("%lld
    ",ans);
        }
    }P;
    int main()
    {
        P.read();
        P.solve();
    }
    View Code

    https://www.luogu.org/problem/P4287

    计算串的最长双倍回文子串的长度,tips:fail指针指向当前节点所表示的回文串的最长回文后缀

    #include<stdio.h>
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=5e5+10;
    struct pam_trie
    {
        int ch[26];
        int fail,len,sum;
    };
    struct pam
    {
        pam_trie b[maxn];
        int n,length,last,cnt,s[maxn];
        char c[maxn];
        pam()
        {
            b[0].len=0;b[1].len=-1;
            b[0].fail=1;b[1].fail=0;
            last=0;cnt=1;
        }
        void read()
        {
            scanf("%d",&length);
            scanf("%s",c+1);
        }
        int get_fail(int x)
        {
            while(s[n-b[x].len-1]!=s[n])x=b[x].fail;
            return x;
        }
        void insert()
        {
            int p=get_fail(last);
            if(!b[p].ch[s[n]])
            {
                b[++cnt].len=b[p].len+2;
                b[cnt].fail=b[get_fail(b[p].fail)].ch[s[n]];
                b[p].ch[s[n]]=cnt;
            }
            last=b[p].ch[s[n]];
            b[last].sum++;
        }
        void solve()
        {
            s[0]=26;
            for(n=1;n<=length;n++)
            {
                s[n]=c[n]-'a';
                insert();
            }
            int ans=0;
            for(int i=cnt;i>0;i--)
            {
                int pos=i;
                if(b[i].len%4!=0||b[i].len<=ans)continue;
                while(2*b[pos].len>b[i].len)pos=b[pos].fail;
                if(2*b[pos].len==b[i].len)ans=b[i].len;
            }
            printf("%d
    ",ans);
        }
    }P;
    int main()
    {
        P.read();
        P.solve();
        return 0;
    }
    View Code

    ICPC 2018 南京 Mediocre String Problem,用回文自动机来求出以每个位置结尾的回文子串个数,再进行exkmp

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    const int maxn=1e6+10;
    struct pam_trie
    {
        int ch[26];
        int fail,len,num;
    };
    int res[maxn];
    char s1[maxn],s2[maxn],t[maxn];
    
    struct pam
    {
        pam_trie b[maxn];
        int n,last,cnt,s[maxn],length;
        pam()
        {
            b[0].len=0;b[1].len=-1;
            b[0].fail=1;b[1].fail=0;
            last=0;
            cnt=1;
        }
        int get_fail(int x)
        {
            while(s[n-b[x].len-1]!=s[n])x=b[x].fail;
            return x;
        }
        int insert()
        {
            int p=get_fail(last);
            if(!b[p].ch[s[n]])
            {
                b[++cnt].len=b[p].len+2;
                b[cnt].fail=b[get_fail(b[p].fail)].ch[s[n]];
                b[p].ch[s[n]]=cnt;
            }
            last=b[p].ch[s[n]];
            b[last].num=b[b[last].fail].num+1;
            return b[last].num;
        }
        void solve()
        {
            s[0]=26;
            length=strlen(s1);
            for(n=1;n<=length;n++)
            {
                s[n]=s1[n-1]-'a';
                res[n-1]=insert();
            }
        }
    }P;
    
    int Next[maxn],extend[maxn];
    void get_next(char *s)
    {
        int n=strlen(s),i,j,k=1;
        for(j=0;1+j<n&&s[j]==s[1+j];j++);
        Next[1]=j;
        for(i=2;i<n;i++)
        {
            int len=k+Next[k],L=Next[i-k];
            if(L<len-i)Next[i]=L;
            else 
            {
                for(j=max(0,len-i);i+j<n&&s[j]==s[i+j];j++);
                Next[i]=j;
                k=i;
            }
        }
        Next[0]=n;    
    }
    void ex_kmp(char *T,char *s)
    {
        int n=strlen(T),m=strlen(s),i,j,k;
        for(j=0;j<n&&j<m&&T[j]==s[j];j++);
        extend[0]=j;
        k=0;
        for(i=1;i<n;i++)
        {
            int len=k+extend[k],L=Next[i-k];
            if(L<len-i)extend[i]=L;
            else
            {
                for(j=max(0,len-i);j<m&&i+j<n&&s[j]==T[i+j];j++);
                extend[i]=j;
                k=i;
            }
        }
    }
    
    
    int main()
    {
        scanf("%s",s1);
        scanf("%s",t);
        int lens=strlen(s1);
        
        reverse(s1,s1+lens);
        for(int i=0;i<lens;i++)s2[i]=s1[i];
        s2[lens]='';
        
        get_next(t);
        ex_kmp(s2,t);
        long long ans=0;
        P.solve();
        for(int i=1;i<lens;i++)
        {
            ans+=1ll*extend[i]*res[i-1]; 
        }
        printf("%lld
    ",ans);    
        return 0;
    } 
    View Code

    ...

  • 相关阅读:
    漫画 | 一台Linux服务器最多能支撑多少个TCP连接?
    frida hook pthread_create
    mac搜索so中的字符串
    字节字符串和数字互转
    twsited使用延迟项,并发量限制
    常见汇编指令和EFLAGS寄存器对应位的含义
    scrapy设置cookie的三种方式
    G1GC 读书笔记
    SLF4J 适配图
    支付宝和微信提现有办法免费了!
  • 原文地址:https://www.cnblogs.com/myrtle/p/11619286.html
Copyright © 2011-2022 走看看