NOIP2020 字符串匹配

题目描述

给定一个字符串,求满足 \(S=(AB)^iC\),要求 \(A,B,C\) 均不为空串,且 \(F(A)\le F(C)\),其中 \(F(A)\) 表示 \(A\) 中出现奇数次的字符数量。

数据范围:\(\vert S\vert\le2^{20}\)

时间范围:\(1000\operatorname{ms}\)

Solution

stOHXPOrz
\(\Theta(\infty)\sim\Theta(n\sqrt n)\) 的就不说了。
主要是补充一下这篇题解
太菜了没想到/kk

这篇题解概括一遍:

  • 由于用到子串匹配,考虑字符串哈希。(后缀数组也不是不可以)
  • 考虑如何满足 \(F(A)\le F(C)\)。可以发现,当 \(AB\) 的长度固定时,我们可以通过枚举倍数来达到枚举 \(F(C)\) 的目的,可以开树状数组维护 \(F(A)\)(想写值域分块、\(01\operatorname{Trie}\) 或者平衡树也不拦你)。
    时间复杂度为 \(\Theta(n\ln n\log 26)\),期望得分 \(84\operatorname{pts}\)
  • 再观察亿下,可以发现对于同一个 \(AB\) 循环的若干倍,\(F(C)\) 只有两个取值,可以快速求出循环次数,然后分类讨论。

那么问题来了:怎么快速求出循环次数?

从高到低枚举,处理 \(i\) 的循环次数时可以用 \(2i\) 的循环次数来处理,然后判断下一个循环节是否相同。

时间复杂度为 \(\Theta(n\log 26)\),期望得分 \(100\operatorname{pts}\)

Code

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1200000;
const unsigned MLY=133;
template<class T>inline T Max(const T &a,const T &b){return a>b?a:b;}
char s[maxn];
int n;
struct TreeArray{
	int tr[28];
	#define lowbit(x) (x&-x)
	inline void add(int x,int d){
		++x;while(x<=27)tr[x]+=d,x+=lowbit(x);
	}
	inline int ask(int x){
		++x;
		int ans=0;
		while(x)ans+=tr[x],x-=lowbit(x);
		return ans;
	}
	inline void clear(){
		memset(tr,0,sizeof(tr));
	}
}BIT;
struct StringHash{
	unsigned long long hash[maxn],base[maxn];
	inline StringHash(){
		base[0]=1;for(int i=1;i<maxn;++i)base[i]=base[i-1]*MLY;
	}
	inline void Init(){
		for(int i=1;i<=n;++i)hash[i]=hash[i-1]*MLY+s[i];
	}
	inline unsigned long long ask(int l,int r){
		return hash[r]-hash[l-1]*base[r-l+1];
	}
}Hash;
int pre[maxn],suf[maxn],cnt[300],rep[maxn];
bool vis[27];
int stk[27],top;
int main(){
	FILE *fin=stdin;
	FILE *fout=stdout;
	int T;
	fscanf(fin,"%d",&T);
	while(T--){
		memset(pre,0,sizeof(pre));
		memset(suf,0,sizeof(suf));
		fscanf(fin,"%s",s+1);
		n=strlen(s+1);
		Hash.Init();BIT.clear();
		memset(cnt,0,sizeof(cnt));
		for(int i=1;i<=n;++i)pre[i]=pre[i-1]+(((++cnt[s[i]])&1)?1:-1);
		memset(cnt,0,sizeof(cnt));
		for(int i=n;i;--i)suf[i]=suf[i+1]+(((++cnt[s[i]])&1)?1:-1);
		long long ans=0;
		for(int i=n-1;i;--i){
			if(i*2>=n)rep[i]=1;
			else if(Hash.ask(1,i)!=Hash.ask(i+1,i*2))rep[i]=1;
			else{
				rep[i]=rep[i*2]*2;
				if(i*(rep[i]+1)<n&&Hash.ask(1,i)==Hash.ask(i*rep[i]+1,i*(rep[i]+1)))++rep[i];
			}
		}
		for(int i=1;i<n;++i){
			if(rep[i]==1){
				ans+=BIT.ask(suf[i+1]);
			}
			else{
				ans+=(rep[i]+1)/2ll*BIT.ask(suf[i+1]);
				ans+=rep[i]/2ll*BIT.ask(suf[i*2+1]);
			}
			BIT.add(pre[i],1);
		}
		fprintf(fout,"%lld\n",ans);
	}
	return 0;
}
posted @ 2021-05-20 18:57  7103  阅读(224)  评论(0)    收藏  举报