题目
题目描述
读入一个字符串 SS, 问该字符串有多少对子串 u,vu,v, uu 和 vv 都是回文串且出现位置相交.
答案对 998244353 取模.
输入格式
输入只有一行, 包括一个字符串 SS, 仅包含小写字符.
输出格式
输出只有一个整数, 表示答案对 998244353 取模后的值.
样例 1
输入
babb
输出
6
说明
在样例中, 回文子串有 S[1,1],S[2,2],S[3,3],S[4,4],S[1,3],S[3,4]S[1,1],S[2,2],S[3,3],S[4,4],S[1,3],S[3,4].
其中 S[1,1],S[2,2],S[3,3],S[3,4]S[1,1],S[2,2],S[3,3],S[3,4] 和 S[1,3]S[1,3] 相交, S[3,3],S[4,4]S[3,3],S[4,4] 和 S[3,4]S[3,4] 相交.
第 11 个点 |S|≤1000|S|≤1000, 第 2,32,3 个点 |S|≤100000|S|≤100000, 第 4,54,5 个点 |S|≤2000000|S|≤2000000.
思路
把串的回文树求出来之后, 就相当于判断一下每个串的开头是否是 0 0 0, 以及统计每个串的出现次数. 典型的树上操作.
代码
#include<bits/stdc++.h>
#define re register
#define FOR(i,a,b) for(re int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(re int i=(a);i>=(b);i--)
using namespace std;
typedef long long ll;
const int N=int(2e6)+10;
const ll mo=ll(998244353);
int n;
char ch[N],s[N*2];
int yjy[N*2];
ll f[N*2],g[N*2];
int main(){
scanf("%s",ch+1);
n=strlen(ch+1);
FOR(i,1,n)
s[i*2]=ch[i],s[i*2-1]='#';
s[n*2+1]='#',n=n*2+1;
int aii=0,P=0;//aiiter,right P
FOR(i,1,n){
yjy[i]=i<P?min(yjy[2*aii-i],P-i+1):1;
while(i+yjy[i]<=n && i-yjy[i]>=1 && s[i+yjy[i]]==s[i-yjy[i]])
yjy[i]++;
if(i+yjy[i]>P)
aii=i,P=i+yjy[i]-1;
}
ll suf=0,ans=0;
FOR(i,1,n){
f[i-yjy[i]+1]++,f[i+1]--;
g[i]++,g[i+yjy[i]]--;
ans=(ans+yjy[i]/2)%mo;
}
ans=ans*(ans-1)/2%mo;
FOR(i,1,n){
f[i]+=f[i-1],g[i]+=g[i-1];
if(s[i]!='#')
ans=(ans-suf*f[i]%mo+mo)%mo,suf=(suf+g[i])%mo;
}
printf("%lld",ans);
return 0;
}