题解: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;
}
由于 CF 的 hack 数据太强了,连双哈希都卡,作者使用了一个特殊的 base 值。
浙公网安备 33010602011771号