题解:CF1984D "a" String Problem

题意

给定一个字符串 \(s\),求它有多少个不同的子串 \(t\) 满足可以把 \(s\) 分割成若干个子串,使得每个子串要么等于 \(t\),要么等于 \(\texttt{"a"}\)。

思路

首先如果字符串全部由 a 组成那就直接输出 \(|s|-1\) 即可。

接下来考虑正常情况,由于分割完后非 \(\texttt{"a"}\) 子串必须完全相同,且这些子串的非 a 部分必须包含整个 \(s\) 中所有的非 a 字符,所以我们可以先把 \(s\) 中的所有的非 a 字符的下标取出来存放。记这些字符的数量为 \(k\),由于子串 \(t\) 中非 a 字符的数量必须相同,所以我们可以枚举 \(k\) 的因数(记为 \(x\))作为 \(t\) 中非 a 字符的数量,通过哈希判断这 \(\frac{k}{x}\) 段每段下标头尾对应原串 \(s\) 的子串是否相同,若相同再计算这个子串前 / 后能多挂 a 的方案数,所有枚举得到的答案相加即可。

代码

#include<bits/stdc++.h>
#define int long long
#define MAXN 1000005
using namespace std;
const int inf=1e18,base=19491001,mod=998244353;
int T,n,ans,d[MAXN],dcnt;
int has[MAXN],p[MAXN];
int qhas(int l,int r){
	return (has[r]-has[l-1]*p[r-l+1]%mod+mod)%mod;
}
char c[MAXN];
int check(int len){
	int thas=qhas(d[1],d[len]);
	for(int i=len+1;i<=dcnt;i+=len){
		if(qhas(d[i],d[i+len-1])!=thas)return 0;//判断是否相同 
	}
	int minh=inf;
	for(int i=len+1;i<=dcnt;i+=len){
		minh=min(minh,d[i]-d[i-1]-1);//计算每段之间a数量的最小值,也就是每段前后能挂a的数量之和的最小值 
	}
	int sum=0;
	for(int i=0;i<d[1];i++){//枚举前面能挂几个a 
		if(minh-i<0)break;
		sum+=min(minh-i,n-d[dcnt])+1;//n-dcnt是每段后能挂a的最小值 
	}
	return sum;
}
signed main(){
//	freopen("aa.in","r",stdin);
//	freopen("aa.out","w",stdout);
	scanf("%lld",&T);
	p[0]=1;
	for(int i=1;i<=MAXN-5;i++)p[i]=p[i-1]*base%mod;
	while(T--){
		scanf("%s",c+1);
		n=strlen(c+1);
		ans=dcnt=0;
		for(int i=1;i<=n;i++){
			has[i]=(has[i-1]*base%mod+(c[i]-'a'+1))%mod;//哈希 
			if(c[i]!='a'){
				d[++dcnt]=i;//记录非a字符下标 
			}
		}
		if(!dcnt){
			printf("%lld\n",n-1);
			continue;
		}
		for(int i=1;i*i<=dcnt;i++){
			if(dcnt%i)continue;//枚举因数 
			ans+=check(i);
			if(i*i!=dcnt)ans+=check(dcnt/i);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

AC 记录

由于 CF 的 hack 数据太强了,连双哈希都卡,作者使用了一个特殊的 base 值。

posted @ 2025-11-02 22:12  CharlieCai2024  阅读(21)  评论(0)    收藏  举报