P7114 [NOIP2020] 字符串匹配 题解

问题描述

。。。我不太想说 NOIP2020噩梦

P7114 [NOIP2020] 字符串匹配

问题解决

君子报仇,十年不晚

这道题考场上写了一个(O(nlognlog26))的解法,不够优秀被卡掉了

后面看了题解,然后写了一个(O(nlog26))的解法

引入拓展(kmp)中的(z)函数:(z[i])表示(s[i,n-1])(s)本身的最长公共前缀的长度

先不考虑奇数,对于循环节为(i)的字符串,可以计算上的就是(leftlfloordfrac{z[i]}{i} ight floor +1)

然后对于 (k) 分奇偶讨论,我们发现奇数和偶数满足的条件是相同的,于是乘法原理加上树状数组统计一下就好了

代码实现

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=(1<<20)+5;
char s[maxn];
int n,z[maxn],before[30],after[30],pre,suf,all;
int c[30],T;
inline void add_x(int x,int data){
	for(int i=x;i<=27;i+=i&-i)c[i]+=data;
}
inline int get(int x){
	int ret=0;
	for(int i=x;i;i-=i&-i)ret+=c[i];
	return ret;
}
void Z(){
	z[0]=n; int now=0;
	while(now+1<n&&s[now]==s[now+1])now++;
	z[1]=now;int p0=1;
	for(int i=2;i<n;i++){
		if(i+z[i-p0]<p0+z[p0]) z[i]=z[i-p0];
		else {
			now=p0+z[p0]-i;now=max(now,0);
			while(now+i<n&&s[now]==s[now+i])now++;
			z[i]=now;p0=i;
		}
	}
}

int main(){
	freopen("P7114.in","r",stdin);
	freopen("P7114.out","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%s",s);
		n=strlen(s);
		memset(before,0,sizeof before);
		memset(after,0,sizeof after);
		memset(c,0,sizeof c);
		all=pre=suf=0;Z();
		for(int i=0;i<n;i++) i+z[i]==n&&(z[i]--,0);
		for(int i=0;i<n;i++) after[s[i]-'a']++;
		for(int i=0;i<26;i++) (after[i]&1)&&(all++,0);
		suf=all;LL ans=0;
		for(int i=0;i<n;i++){
			if(after[s[i]-'a']&1)suf--;else suf++;after[s[i]-'a']--;
			if(before[s[i]-'a']&1)pre--;else pre++;before[s[i]-'a']++;
			if(i!=0&&i!=n-1){
				int t=z[i+1]/(i+1)+1;
				ans+=1LL*(t/2)*get(all+1)+1LL*(t-t/2)*get(suf+1);
			}
			add_x(pre+1,1);
		}
		printf("%lld
",ans);
	}
}
原文地址:https://www.cnblogs.com/martian148/p/15340067.html