【noi.ac #1765】 T2

题目

题目描述
读入一个字符串 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值