牛客多校第四场 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\n",s);
//    printf("%s\n",ss);
    sprintf(ss+strlen(ss),"%s\0",s);
    int len1=strlen(ss);
//    printf("%s\n",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\n",ans);
    
    ans-=2LL*len+1+1LL*len*len;
    ans+=PTnum-1;
    
    assert(ans%2==0);
    printf("%lld\n",ans/2);
}

 

posted @ 2019-07-28 00:01  Isakovsky  阅读(305)  评论(0编辑  收藏  举报