题解 NOIP2020 字符串匹配
题解 NOIP2020 字符串匹配
感谢 @slzs
[NOIP2020] 字符串匹配
先枚举单个 \(AB\) 的总长度,有了很厉害的Z函数我们就可以 \(O(1)\) 求出 \(AB\) 的个数 \(k\)。我们可以预处理出 \(S\) 串的前缀的奇偶个数和后缀的奇偶个数。然后我们再确定 \(C\) 的奇偶性情况,无疑就是 \(C=ABC\) 或 \(C=C\) 两种情况,所以我们分类讨论一下就好啦。最后确定 \(A\) 的奇偶性,我们可以用树状数组来找,先把前缀的奇数个数放进树状数组里面,然后用 \(C\) 的奇数个数来找出有几种情况。最后分类一下,当 \(C=ABC\) 时,有 \(\lfloor\frac{k}{2}\rfloor\) 种等奇数个数的情况;当 \(C=C\) 时,有 \(\lfloor{\frac{k+1}{2}}\rfloor\) 种等奇数个数的情况,乘上 \(A\) 的情况数就好啦 (≧▽≦)/
#include<bits/stdc++.h>
#define lowbit(x) ((x)&(-x))
using namespace std;
const int N=(1<<20)+5,INF=1e9;
int T;
int n;
int tre[35];
void upd(int x){while(x<=30){tre[x]++;x+=lowbit(x);}}
int ask(int x){int res=0;while(x){res+=tre[x];x-=lowbit(x);}return res;}
char t[N];
int z[N];
void doZ(){
z[1]=n;
for(int i=2,l=0,r=0;i<=n;i++){
if(i<=r) z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n&&t[i+z[i]]==t[z[i]+1]) ++z[i];
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}
}
int pre[N],pos[N],cnt[26];
void doParity(){
for(int i=0;i<26;i++) cnt[i]=0;
for(int i=1;i<=n;i++){
++cnt[t[i]-'a'];
if(cnt[t[i]-'a']&1) pre[i]=pre[i-1]+1;
else pre[i]=pre[i-1]-1;
}
for(int i=0;i<26;i++) cnt[i]=0;
for(int i=n;i>=1;i--){
++cnt[t[i]-'a'];
if(cnt[t[i]-'a']&1) pos[i]=pos[i+1]+1;
else pos[i]=pos[i+1]-1;
}
}
int main(){
cin>>T;
while(T--){
cin>>t+1;
n=strlen(t+1);
memset(tre,0,sizeof(tre));
memset(z,0,sizeof(z));
memset(pos,0,sizeof(pos));
memset(pre,0,sizeof(pre));
doZ();
doParity();
long long res=0;
for(int i=2;i<n;i++){
int k=min(z[i+1]+i,n-1)/i;
upd(pre[i-1]+1);
res+=1ll*ask(pos[k*i+1]+1)*((k+1)/2);
res+=1ll*ask(pos[k*i+1-i]+1)*(k/2);
}
printf("%lld\n",res);
}
return 0;
}

浙公网安备 33010602011771号