[NOIP2020] 字符串匹配

题目链接

大致题意

给一个字符串\(S\),求\(S=(AB)^iC\)的方案数,其中\(F(A)≤F(C)\),\(F(S)\)表示字符串 \(S\) 中出现奇数次的字符的数量

分析

\(cnt_i\)表示奇数字符数小于等于\(i\)\(A\)的个数

枚举\(AB\)的长度,然后用\(KMP\)\(next\)数组去枚举循环次数\(i\),将小于等于\(C\)中奇数字符的数量的\(A\)的方案数,也就是\(cnt_{j}\)(\(j\)\(C\)中出现奇数次字符从数量)加到答案中,并更新一下\(cnt\)

最坏时间复杂度\(O(n×(logn+26))\),不开\(O2\) \(92\)~\(96\)pts,开\(O2\)稳过

\(code\)

/*
xcxc82
*/
#include<bits/stdc++.h>
using namespace std;
const int MAXN = (1<<20)+10;
inline int read(){
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;return ~(X-1);
}
int pre[MAXN],suf[MAXN],next[MAXN],pre_value[30];
int buc[30];
bool vis[30];
int T,n;
long long ans;
string s;
void clear(){
	ans = 0;
	memset(vis,false,sizeof(vis));
	memset(pre,0,sizeof(pre));
	memset(suf,0,sizeof(suf));
	memset(next,0,sizeof(next));
	memset(buc,0,sizeof(buc));
	memset(pre_value,0,sizeof(pre_value));
} 
void kmp(){
	int j = 0;
	for(int i=2;i<=n;i++){
		while(j>0&&s[i]!=s[j+1]) j = next[j];
		if(s[i]==s[j+1]) j++;
		next[i] = j;
	}
}
signed main(){
	T = read();
	while(T--){
		int cnt = 0;
		clear();
		cin>>s;
		s = ' '+s;
		n = s.length()-1;
		for(int i=1;i<=n;i++){
			if(!vis[s[i]-'a'+1]) vis[s[i]-'a'+1] = 1,cnt++;
		}
		kmp();
		for(int i=1;i<=n;i++){
			int now = s[i]-'a'+1;
			buc[now]++;
			if(buc[now]&1) pre[i] = pre[i-1]+1;
			else pre[i] = pre[i-1]-1;
		}
		memset(buc,0,sizeof(buc));
		for(int i=n;i>=1;i--){
			int now = s[i]-'a'+1;
			buc[now]++;
			if(buc[now]&1) suf[i] = suf[i+1]+1;
			else suf[i] = suf[i+1]-1;
		}
		for(int i=1;i<n;i++){
			if(i>1){
				for(int j=i;j<n;j+=i){
					if((i%(j-next[j])==0&&j/(j-next[j])>1)||(j==i))
					ans+=(long long)(pre_value[suf[j+1]]);
					else break;
				}
			}
			for(int j=pre[i];j<=cnt;j++){
				pre_value[j]++;
			}
		}
		printf("%lld\n",ans);
	}
   return 0;
}





posted @ 2021-01-08 20:02  xcxc82  阅读(274)  评论(1编辑  收藏  举报