“蔚来杯“2022牛客暑期多校训练营9-G Magic Spells

"蔚来杯"2022牛客暑期多校训练营9-G Magic Spells

原题题面:https://ac.nowcoder.com/acm/contest/33194/G

题目大意

求字符串集合中所有子串中不同回文串的数量。

解题思路

本题中所有字符串长度不会超过 3 × 1 0 5 3\times 10^5 3×105,所以本质不同的回文串最多有 3 × 1 0 5 3\times 10^5 3×105.

先对第一个字符串进行处理,通过 M a n a c h e r Manacher Manacher求出回文串,通过 H a s h Hash Hash存储回文串(建议使用双模数)。

之后同样用 M a n a c h e r Manacher Manacher求出回文串,同时可以用 s e t set set去重,并添加不同的回文串。

时间复杂度 O ( ∣ S ∣ l o g ∣ S ∣ ) O(|S|log|S|) O(SlogS)

代码实现

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int m1=1e9+7,m2=1e8+7,base=257,N=6e5+5;
int n,p[N],pre1[N],pre2[N],p1[N],p2[N],z;
string a[10];
set<ll>se;
int w1(int l,int r){//hash
	return (pre1[r]-1ll*pre1[l-1]*p1[r-l+1]%m1+m1)%m1;
}
int w2(int l,int r){//hash
	return (pre2[r]-1ll*pre2[l-1]*p2[r-l+1]%m2+m2)%m2;
}
void init(){
	string S="*#";
	for(int i=0;i<a[1].size();i++)
	S+=a[1][i],S+="#";
	S+=")";
	p1[0]=p2[0]=1;
	for(int i=1;i+1<S.size();++i){
		p1[i]=1ll*p1[i-1]*base%m1;
		p2[i]=1ll*p2[i-1]*base%m2;
		pre1[i]=(1ll*pre1[i-1]*base+S[i])%m1;
		pre2[i]=(1ll*pre2[i-1]*base+S[i])%m2;
	}
	for(int i=1,id=0,pm=0;i+1<S.size();i++){
		if(i<pm)p[i]=min(pm-i,p[2*id-i]);
		else p[i]=0;
		while(S[i+p[i]]==S[i-p[i]]){
			ll sum=(ll)w1(i-p[i],i+p[i])*m2+w2(i-p[i],i+p[i]);
			if(S[i+p[i]]=='#')se.insert(sum);
			p[i]++;
		}
		if(i+p[i]>pm)id=i,pm=i+p[i];
	}
}
void solve(int k){
	string S="*#";
	for(int i=0;i<a[k].size();i++)
	S+=a[k][i],S+="#";
	S+=")";
    p1[0]=p2[0]=1;
	for(int i=1;i+1<S.size();++i){
		p1[i]=1ll*p1[i-1]*base%m1;
		p2[i]=1ll*p2[i-1]*base%m2;
		pre1[i]=(1ll*pre1[i-1]*base+S[i])%m1;
		pre2[i]=(1ll*pre2[i-1]*base+S[i])%m2;
	}
	set<ll>Se;
	for(int i=1,id=0,pm=0;i+1<S.size();i++){
		if(i<pm)p[i]=min(pm-i,p[2*id-i]);
		else p[i]=0;
		while(S[i+p[i]]==S[i-p[i]]){
			ll sum=(ll)w1(i-p[i],i+p[i])*m2+w2(i-p[i],i+p[i]);
			if(S[i+p[i]]=='#'&&se.count(sum))Se.insert(sum);
			p[i]++;
		}
		if(i+p[i]>pm)id=i,pm=i+p[i];
	}
	se=Se;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	init();
	for(int i=2;i<=n;i++)solve(i);
	cout<<se.size()-1;//去掉“#”
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值