七海ノ心象素描

所有的为时已晚都是恰逢其时

Nanami^2 avatar

Author

Nanami^2

大一学生 | 不是很聪明的样子

Telegram

我的频道

不定期推送灵感笔记

t.me/kisanami_blog
联系方式
QQ 群
1072614878
发送邮件

LOJ#3387「NOIP2020」字符串匹配 题解

Solution

Nanami^2
Nanami^2 Even in the rain.
2022年08月20日
预计阅读 4 分钟
795 字

Solution

NOIP 多少年来第一道字符串题。也希望是最后一道

这里采用哈希做法。

枚举 ii,表示 AB=S[1,i]AB = S[1,i]。然后倍增地找到最大的 kk,满足 (AB)q(AB)^q 合法,设 (AB)q=S[1,p](AB)^q = S[1,p]

那么 CC 一定是 S[p+1,n]S[p+1,n] 前面有奇数个或偶数个 ABAB。不难发现有偶数个 ABAB 时,其 FF 值必然相同,因此只需要多考虑奇数个的情况,取 S[pi+1,n]S[p-i+1,n] 即可。

对于一个 (p,q)(p,q),放偶数个 ABAB 的情况有 c0=q2c_0=\lceil \frac{q}{2} \rceil 种,奇数个有 c1=qc0c_1=q-c_0 种。注意特判 p=np=n 时,不能放 00ABAB,所以此时 c0c_0 减去 11,后缀需要取 S[p2i+1]S[p-2i+1]。还可能存在 p2i+1<ip-2i+1<i,这是不合法的。

设放偶数个 ABAB 对应的 FF 值为 p0p_0,奇数个为 p1p_1。贡献就是

c0×j=1i1[F(S[1,j])p0]+c1×j=1i1[F(S[1,j])p1]c_0 \times \sum_{j=1}^{i-1} [F(S[1,j])\le p_0] + c_1 \times \sum_{j=1}^{i-1}[F(S[1,j]) \le p_1]

树状数组维护即可。复杂度 O(nlog2n)O(n \log_2 n)

zz 函数做法慢很多,但是好想且不容易写错。另外还存在哈希+调和级数枚举做法,但是笔者把这份倍增代码改为上述做法后,在洛谷上 TLE 了。可能是人傻常数大吧。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define SET(a,b) memset(a,b,sizeof(a))
#define CPY(a,b) memcpy(a,b,sizeof(b))
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define per(i,j,k) for(int i=(j);i>=(k);--i)
int read() {
int a=0, f=1; char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) a=a*10+c-'0', c=getchar();
return a*f;
}
const int N=1048580;
const uint P=1610612741;
int T, n, ans, c[30], pre[N], suf[N], pos[N];
uint h[N], PP[N], f[N][21];
char s[N];
uint getlr(int l,int r) {
return h[r]-h[l-1]*PP[r-l+1];
}
struct BIT {
int c[N];
void insert(int x,int y) {
++x;
for(;x<=n+1;x+=x&-x) c[x]+=y;
}
int query(int x) {
int y=0;
++x;
for(;x;x-=x&-x) y+=c[x];
return y;
}
} Tr;
void iinit() {
SET(c,0);
pre[1]=1, ++c[s[1]-'a'+1];
h[1]=s[1]-'a'+1;
PP[0]=1, PP[1]=P;
for(int i=2;i<=n;++i) {
h[i]=h[i-1]*P+(s[i]-'a'+1);
PP[i]=PP[i-1]*P;
if(c[s[i]-'a'+1]%2==0) pre[i]=pre[i-1]+1;
else pre[i]=pre[i-1]-1;
++c[s[i]-'a'+1];
}
SET(c,0);
suf[n]=1, ++c[s[n]-'a'+1];
for(int i=n-1;i;--i) {
if(c[s[i]-'a'+1]%2==0) suf[i]=suf[i+1]+1;
else suf[i]=suf[i+1]-1;
++c[s[i]-'a'+1];
f[i][0]=h[i];
for(int j=1;i*(1<<j)<=n;++j) {
f[i][j]=f[i][j-1]*(PP[i*(1<<(j-1))]+1);
if(i*(1<<(j+1))>n) pos[i]=j;
}
}
}
pair<int,int> calc(int i) {
int p=0, q=0, j=pos[i];
for(;~j;--j) {
int t=i*(1<<j);
if(p+t>n) continue;
if(f[i][j]==getlr(p+1,p+t)) p+=t, q|=1<<j;
}
return make_pair(p,q);
}
void solve() {
ans=0;
scanf("%s",s+1);
n=strlen(s+1);
memset(Tr.c,0,(n+1)<<3);
iinit();
rep(i,2,n-1) {
Tr.insert(pre[i-1],1);
pair<int,int> t=calc(i);
int p=t.first, q=t.second;
int c0=(q+1)/2, c1=q-c0;
int p0=suf[p+1], p1=suf[p-i+1];
if(p==n) {
--c0;
if(p-2*i>=i) p0=suf[p-2*i+1];
else p0=0;
}
ans+=c0*Tr.query(p0)+c1*Tr.query(p1);
}
printf("%lld\n",ans);
}
signed main() {
T=read();
while(T--) solve();
}

觉得这篇文章怎么样?

点个赞,让更多人看到!

分享这篇文章

知识因分享而增值

分类

题解

标签

字符串
树状数组
多项式哈希
倍增

版权声明:本文作者为 Nanami^2,首发于nanami7.top

遵循 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

评论区

本评论区由 EveSunMaple 自主开发