Codeforces 1909G - Pumping Lemma

这个题思考角度很多,做法也很多。这里介绍一种 @asmend 和我讲的做法。

d=mn,那么我们枚举 |x|=i,|y|=j,设 s,t 的 LCP 长为 l1,LCS 长为 l2,那么可以得到这组 (i,j) 合法的充要条件是:

  • il1
  • mijdl2
  • dmodj=0
  • t[i,i+d1]=t[i+j,i+j+d1]

考虑第四条限制,我们将所有长度为 d 的子串的哈希值排序,这样对于每种子串,我们可以知道其出现的位置 p1,p2,,pc。考虑 occurrence 序列的一个性质:对于极长的满足 pl+1pld,pl+2pl+1d,,prpr1d 的连续段 [l,r],必然有 pl+1pl=pl+2pl+1==prpr1,这个可以用 WPL 证明。这样考虑对每个这样的连续段统计贡献,设等差数列的公差为 t,长度为 c,因为是等差数列,因此这个连续段内部可能出现的 j 只有 t,2t,,(c1)tO(c) 种,枚举之,那么第一、二条限制的作用是将 i 限制在一个区间内,这样相当于求等差数列中在这个区间内的数的个数,这容易 O(1)。因为 c=O(n),所以后面复杂度为线性。复杂度瓶颈在排序,如果使用三个与 n 同阶的质数作为哈希模数并桶排则可以线性。但我直接 sort 以 1996ms 卡了过去就没管了(

const int MAXN=1e7;
const int MOD1=1e9+21;
const int MOD2=1e9+33;
const int BS1=191;
const int BS2=193;
int qpow(int x,int e,int mod){int ret=1;for(;e;e>>=1,x=1ll*x*x%mod)if(e&1)ret=1ll*ret*x%mod;return ret;}
char s[MAXN+5],t[MAXN+5];int n,m,d,pw1,pw2;
struct _hash{
int hs1,hs2,pos;
_hash(){hs1=hs2=pos=0;}
void append(int x){
hs1=(1ll*hs1*BS1+x)%MOD1;
hs2=(1ll*hs2*BS2+x)%MOD2;
}
void del(int x){
hs1=(hs1+1ll*(MOD1-pw1)*x)%MOD1;
hs2=(hs2+1ll*(MOD2-pw2)*x)%MOD2;
}
friend bool operator <(const _hash &X,const _hash &Y){
if(X.hs1!=Y.hs1)return X.hs1<Y.hs1;
if(X.hs2!=Y.hs2)return X.hs2<Y.hs2;
return X.pos<Y.pos;
}
friend bool operator ==(const _hash &X,const _hash &Y){
return (X.hs1==Y.hs1&&X.hs2==Y.hs2);
}
}a[MAXN+5],cur;
int limL,limR;ll res=0;
int calc(int l,int r,int stp,int rem){
if(l>r)return 0;
return (r-rem+stp)/stp-(l-1-rem+stp)/stp;
}
void work(int l,int r){
for(int i=l;i<r;i++)res+=(a[i+1].pos-a[i].pos==d);
for(int L=l,R;L<=r;L=R+1){
if(a[L].pos>limR)break;
R=L;while(R<r&&a[R+1].pos-a[R].pos<d)++R;
if(L!=R){
int d0=a[L+1].pos-a[L].pos;
for(int j=1;j<=R-L;j++)if(d%(j*d0)==0){
int _R=limR,_L=limL-j*d0;
res+=calc(max(_L,a[L].pos),min(_R,a[R-j].pos),d0,a[R].pos%d0);
}
}
}
}
int main(){
scanf("%d%d%s%s",&n,&m,s+1,t+1);d=m-n;
pw1=qpow(BS1,d,MOD1);pw2=qpow(BS2,d,MOD2);
for(int i=1;i<=m;i++){
cur.append(t[i]);
if(i>d)cur.del(t[i-d]);
if(i>=d)a[i-d+1]=cur,a[i-d+1].pos=i-d+1;
}
sort(a+1,a+m-d+2);
// for(int i=1;i<=m-d+1;i++)printf("(%d,%d,%d) %d\n",a[i].hs1,a[i].hs2,a[i].hs3,a[i].pos);
int len1=0,len2=0;
while(len1<n&&s[len1+1]==t[len1+1])++len1;
while(len2<n&&s[n-len2]==t[m-len2])++len2;
limR=len1+1;limL=m-len2-d+1;
for(int l=1,r;l<=m-d+1;l=r){
r=l;while(r<=m-d+1&&a[r]==a[l])++r;
work(l,r-1);
}
printf("%lld\n",res);
return 0;
}
posted @   tzc_wk  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2020-12-26 洛谷 P7156 - [USACO20DEC] Cowmistry P(分类讨论+trie 树上 dp)
2020-12-26 洛谷 P7155 [USACO20DEC] Spaceship P(dp)
2020-12-26 洛谷 P7154 - [USACO20DEC] Sleeping Cows P(dp)
2020-12-26 Codeforces 997E - Good Subsegments(线段树维护最小值个数+历史最小值个数之和)
点击右上角即可分享
微信分享提示