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;
}

浙公网安备 33010602011771号