zoukankan      html  css  js  c++  java
  • 牛客多校第四场 I string 后缀自动机/回文自动机

    这个回文自动机的板有问题,它虽然能过这道题,但是在计算size的时候会出锅!

    题意:

    求一个字符串中本质不同的连续子串有几个,但是某串和它反转后的字符串算一个。

    题解:

    要注意的是,一般字符串题中的“反转”,往往和回文串挂钩,反之亦然。

    赛时最后半小时码的这道题,和队友很快发现了可以把字符串构造成s$rev(s)这种形式。在这个串上求出本质不同的连续字串,这样正的和反的就都统计了一遍,再去掉带$的连续子串,共len*(len+2)+1个,再除2就得出了结果。

    但是我们忘了,即便这样反转了一次,回文串也只统计了一次。因此还要加上回文串的数量再除以二。

    用后缀自动机求本质不同字符串数量,用回文自动机求本质不同回文串数量。

    #include<iostream>
    #include<cstring>
    #include<cassert>
    #define MAXN 400010
    #define LL long long
    using namespace std; 
    
    char* strrev(char* str){
        const int l = strlen(str);
        for(int i=0,j=l-1;i<j;i++,j--){
            swap(str[i],str[j]);
        }
        return str;
    }
    char s[MAXN];
    char ss[MAXN];
    int len;
    
    struct SAMNODE{
        int ch[27];
        int len,fa;
        SAMNODE(){memset(ch,0,sizeof(ch));len=0;}
    }SAMdian[MAXN<<1];
    
    int SAMlas=1,SAMtot=1;
    
    void SAMadd(int c){
        int p=SAMlas;int np=SAMlas=++SAMtot;
        SAMdian[np].len=SAMdian[p].len+1;
        for(;p&&!SAMdian[p].ch[c];p=SAMdian[p].fa)SAMdian[p].ch[c]=np;
        if(!p)SAMdian[np].fa=1;//以上为case 1
        else
        {
            int q=SAMdian[p].ch[c];
            if(SAMdian[q].len==SAMdian[p].len+1)SAMdian[np].fa=q;//以上为case 2
            else
            {
                int nq=++SAMtot;SAMdian[nq]=SAMdian[q];
                SAMdian[nq].len=SAMdian[p].len+1;
                SAMdian[q].fa=SAMdian[np].fa=nq; 
                for(;p&&SAMdian[p].ch[c]==q;p=SAMdian[p].fa)SAMdian[p].ch[c]=nq;//以上为case 3
            }
        }
    }
    
    struct PTnode{
        int len,fail,son[26],siz;
        PTnode(){
            len=fail=0;
            for(int i=0;i<=25;i++)
                son[i]=0;
        }
    }PTdian[MAXN<<1];
    
    int PTlast,PTnum;
    
    int PTgetfail(int i,int x){
        while(s[i-PTdian[x].len-1]!=s[i]) {
            x=PTdian[x].fail;
        }
        return x;
    }
    
    void PTextend(int i,int x){
        int cur=PTgetfail(i,PTlast);
        if(!PTdian[cur].son[x]){
            int now=++PTnum;
            PTdian[now].len=PTdian[cur].len+2;
            PTdian[now].fail=PTdian[PTgetfail(i,PTdian[cur].fail)].son[x];
            PTdian[cur].son[x]=now;
        }
        PTdian[PTdian[cur].son[x]].siz++;
        PTlast=PTdian[cur].son[x];
    }
    
    int main(){
        scanf("%s",s);
        len=strlen(s);
        
        PTlast=PTnum=1;
        PTdian[1].len=-1;
        PTdian[0].fail=PTdian[1].fail=1;
        
        for(int i=0;i<len;i++){
            PTextend(i,s[i]-'a');
        }
        
        sprintf(ss,"%s%c",s,'z'+1);
        strrev(s);
    //    printf("%s
    ",s);
    //    printf("%s
    ",ss);
        sprintf(ss+strlen(ss),"%s",s);
        int len1=strlen(ss);
    //    printf("%s
    ",ss);
        
        for(int i=0;i<len1;i++){
            SAMadd(ss[i]-'a');
        }
        
        LL ans=0;
        for(int i=1;i<=SAMtot;i++){
            ans+=SAMdian[i].len-SAMdian[SAMdian[i].fa].len;
        }
    //    printf("%d
    ",ans);
        
        ans-=2LL*len+1+1LL*len*len;
        ans+=PTnum-1;
        
        assert(ans%2==0);
        printf("%lld
    ",ans/2);
    }
  • 相关阅读:
    SQL Server 创建触发器(trigger)
    jQuery插件-json2.js
    Opengl创建机器人手臂代码示例
    OpenGL超级宝典完整源码(第五版)
    基于Opengl的太阳系动画实现
    Opengl创建几何实体——四棱锥和立方体
    ubuntu16.04安装labelme
    Visual Studio Command Prompt 工具配置方法
    OpenNi安装示例
    Opencv读取图片像素值并保存为txt文件
  • 原文地址:https://www.cnblogs.com/isakovsky/p/11257371.html
Copyright © 2011-2022 走看看