Loading

题解 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;
}
posted @ 2021-03-31 13:22  Quick_Kk  阅读(112)  评论(1)    收藏  举报