牛客多校第四场 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); }