CF17E Palisection(回文自动机)

题意翻译

给定一个长度为n的小写字母串。问你有多少对相交的回文子 串(包含也算相交) 。 输入格式

第一行是字符串长度n(1<=n<=2*10^6),第二行字符串 输出格式

相交的回文子串个数%51123987

题解

直接判断相交的回文串很难

那我们考虑找出所有不相交的回文串

数量就是所有以$i$结尾的回文串数乘以$i$后面的回文串数

以$i$结尾的回文串数就是在fail树里$i$的深度

然后$i$后面的回文串数只要倒着做一遍再记录一个后缀和就好了

然后记$sum$为总共的回文串数,回文串对数就是$sum*(sum-1)/2$

然后减一减就好了

 1 //minamoto
 2 #include<cstring>
 3 #include<cstdio>
 4 const int N=2e6+5,P=51123987;
 5 int head[N],Next[N],ver[N],edge[N],E;
 6 int len[N],fail[N],cnt[N],dep[N],suf[N],p,q,tot,last;
 7 char s[N],ss[N];
 8 int n,m,sum,ans;
 9 inline int pls(int x,int y){return x+=y,x>=P?x-P:x;}
10 inline int sub(int x,int y){return x-=y,x<0?x+P:x;}
11 inline int newnode(int x){
12     len[++tot]=x;return tot;
13 }
14 inline int getfail(int x,int n){
15     while(s[n-len[x]-1]!=s[n]) x=fail[x];return x;
16 }
17 inline void init(){
18     s[0]=-1,fail[0]=1,last=n=0;
19     len[0]=0,len[1]=-1,tot=1,E=0;
20     memset(cnt,0,sizeof(cnt));
21 }
22 inline void add(int u,int v,int e){
23     ver[++E]=v,Next[E]=head[u],head[u]=E,edge[E]=e;
24 }
25 inline int get(int u,int e){
26     for(int i=head[u];i;i=Next[i])
27     if(edge[i]==e) return ver[i];return 0;
28 }
29 int ins(int c){
30     s[++n]=c,p=getfail(last,n);
31     if(!get(p,c)){
32         q=newnode(len[p]+2);
33         fail[q]=get(getfail(fail[p],n),c);
34         add(p,q,c),dep[q]=dep[fail[q]]+1;
35     }
36     ++cnt[last=get(p,c)];return dep[last];
37 }
38 inline int calc(){
39     int res=0;
40     for(int i=tot;i>=2;--i)
41     res=pls(res,cnt[i]),cnt[fail[i]]+=cnt[i];
42     return res;
43 }
44 int main(){
45 //    freopen("testdata.in","r",stdin);
46     scanf("%d%s",&m,ss+1);
47     init();for(int i=m;i;--i) suf[i]=pls(suf[i+1],ins(ss[i]-'a'));
48     memset(head,0,sizeof(head));
49     init();for(int i=1;i<=m;++i) ans=pls(ans,1ll*ins(ss[i]-'a')*suf[i+1]%P);
50     sum=calc();
51     sum=1ll*sum*(sum-1)/2%P;
52     printf("%d\n",sub(sum,ans));
53     return 0;
54 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9636583.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值