Description
小 C 学习完了字符串匹配的相关内容,现在他正在做一道习题。
对于一个字符串 $S$,题目要求他找到 $S$ 的所有具有下列形式的拆分方案数:
$S = ABC$,$S = ABABC$,$S = ABAB ldots ABC$,其中 $A$,$B$,$C$ 均是非空字符串,且 $A$ 中出现奇数次的字符数量不超过 $C$ 中出现奇数次的字符数量。
更具体地,我们可以定义 $AB$ 表示两个字符串 $A$,$B$ 相连接,例如 $A = exttt{aab}$,$B = exttt{ab}$,则 $AB = exttt{aabab}$。
并递归地定义 $A^1=A$,$A^n = A^{n - 1} A$($n ge 2$ 且为正整数)。例如 $A = exttt{abb}$,则 $A^3= exttt{abbabbabb}$。
则小 C 的习题是求 $S = {(AB)}^iC$ 的方案数,其中 $F(A) le F(C)$,$F(S)$ 表示字符串 $S$ 中出现奇数次的字符的数量。两种方案不同当且仅当拆分出的 $A$、$B$、$C$ 中有至少一个字符串不同。
小 C 并不会做这道题,只好向你求助,请你帮帮他。
Solution
预处理每个前缀和后缀的奇数字符个数
枚举$AB$的数次方,检验时使用哈希,每次对答案的贡献为$(AB)^i$可以被分成使$A$满足要求的方案数
时间复杂度$O(n log n)$,勉强能过,使用exKMP可以达到$O(n)$,不会
#include<iostream> #include<cstring> #include<cstdio> using namespace std; short T,pre[1050005],suf[1050005],buc[30]; int n,vst[30]; long long ans; unsigned long long base[1050005]={1},has[1050005]; char s[1050005]; inline int read() { int f=1,w=0; char ch=0; while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=getchar(); return f*w; } int main() { T=read(); for(int i=1;i<=1050000;i++) base[i]=base[i-1]*31; for(;T;T--) { scanf("%s",s+1),n=strlen(s+1),ans=0; memset(buc,0,sizeof(buc)),memset(vst,0,sizeof(vst)),memset(suf,0,sizeof(suf)); for(int i=1;i<=n;i++) { buc[s[i]-'a']^=1; if(buc[s[i]-'a']) pre[i]=pre[i-1]+1; else pre[i]=pre[i-1]-1; } memset(buc,0,sizeof(buc)); for(int i=n;i;i--) { buc[s[i]-'a']^=1; if(buc[s[i]-'a']) suf[i]=suf[i+1]+1; else suf[i]=suf[i+1]-1; } for(int i=1;i<=n;i++) has[i]=has[i-1]*31+1ull*(s[i]-'a'); for(int i=2;i<=n;i++) { for(int j=pre[i-1];j<=26;j++) vst[j]++; for(int j=i;j<n&&has[i]==has[j]-has[j-i]*base[i];j+=i) ans+=vst[suf[j+1]]; } printf("%lld ",ans); } return 0; }