LOJ#3387「NOIP2020」字符串匹配 题解
Solution
Nanami^2 Even in the rain.
2022年08月20日
预计阅读 4 分钟
795 字
Solution
NOIP 多少年来第一道字符串题。也希望是最后一道。
这里采用哈希做法。
枚举 ,表示 。然后倍增地找到最大的 ,满足 合法,设 。
那么 一定是 前面有奇数个或偶数个 。不难发现有偶数个 时,其 值必然相同,因此只需要多考虑奇数个的情况,取 即可。
对于一个 ,放偶数个 的情况有 种,奇数个有 种。注意特判 时,不能放 个 ,所以此时 减去 ,后缀需要取 。还可能存在 ,这是不合法的。
设放偶数个 对应的 值为 ,奇数个为 。贡献就是
树状数组维护即可。复杂度 。
比 函数做法慢很多,但是好想且不容易写错。另外还存在哈希+调和级数枚举做法,但是笔者把这份倍增代码改为上述做法后,在洛谷上 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();}觉得这篇文章怎么样?
点个赞,让更多人看到!

评论区