Border 理论学习笔记
Border 理论学习笔记
约定
-
字符串下标从
开始。 -
定义
为 与 这两个字符串的拼接。 -
定义
。 -
定义
,也就是 的前后缀。 -
对于
,若 ,则称 是 在 中的匹配位置。
周期与 border
定义
若存在
称
若存在
也就是说,周期是一个长度,border 是一个字符串。
性质
是 的 border 为 的周期。
证明:
- Weak Periodicity Lemma:对于字符串
,若 为 的周期, ,那么 为 的周期。
证明:不妨设
根据周期定义:
因此,
根据更相减损法的过程,显然
- Periodicity Lemma:对于字符串
,若 为 的周期, ,那么 为 的周期。
不会证捏。
- 对于字符串
,若 ,则 在 中的所有匹配位置构成等差序列。
证明:在匹配位置个数小于
在匹配位置个数大于等于
图中红色部分均为
我们设
那么
假设橙色部分为
那么,因为
border 的结构
- 若
是 的 border, 是 的 border。
border 的 border 是 border。比较显然,就不证了。
的长度不小于 的 border 长度构成一个等差序列。
证明:设
那么
由于
有了整除就和上上个性质很像了。通过与上面类似的分析可以得出该结论。
- 推论:
的所有 border 长度可以组成 个等差数列。
证明:将
我们以每一组中的最长 border 作为基准,那么根据上面的定理,这一组中所有 border 的长度构成等差序列。那么总共就是
子串周期查询
问题:一个长度为
问周期等价于问 border。
我们首先将 border 按照长度分类:
。 。
首先,我们先解决第一种情况。
如图,对于 border
因此,我们找出所有
那么,我们求出所有
考虑情况二,发现本质相同,就是将上面的
因此,我们现在要解决的问题形如:
- 求出
在 中的匹配位置。 - 合并两个等差数列。
我们先考虑问题 1。发现我们要匹配的字符串长度形如
问题二中合并两个等差序列是好解决的,可以通过一些解同余方程之类的东西做到
到这里,我们就能用
能不能更给力?发现瓶颈就是上面的两个问题,考虑优化。
首先对第一个问题进行优化。实际上,我们只在乎形如
发现长为
上面的方法要求匹配区间的长度也是
然后考虑第二个问题。事实上,我们有下面的性质:
- 对于四个字符串满足
, 在 中至少匹配 次, 在 中至少匹配 次,那么 与 最小周期相等。
证明:若上述性质不成立,不妨设
于是,我们的两个等差序列要么其中一个项数小于
至此,我们以
有一道题 [BJWC2018] Border 的四种求法 是求最长 border,可以用来测试上面的问题。
给出
#include<bits/stdc++.h>
using namespace std;
char s[1000010];
int Q,l,r,n,m,cnt;
int ans,bu[1000010],k1[20][200010],p[1000010],sa[1000010];
vector<int> pos[20][200010];
struct Seq{
int st,ed,sum,delta;
}none;
bool In(Seq x,int y){
return !((y-x.st)%x.delta);
}
Seq Merge(Seq x,Seq y){
if(x.delta==y.delta){
if(x.st>y.st) swap(x,y);
if(x.ed<y.st) return {0,0,0,0};
x.st=y.st;x.ed=min(x.ed,y.ed);x.sum=(x.ed-x.st)/x.delta+1;
return x;
}
if(x.sum<y.sum) swap(x,y);
int cnt=0,a[10]={},z=y.st;
for(int i=1;i<=y.sum;i++){
if(In(x,z)) a[++cnt]=z;
z+=y.delta;
}
if(!cnt) return {0,0,0,0};
x.st=a[1],x.ed=a[cnt],x.sum=cnt;
if(cnt>1) x.delta=(x.ed-x.st)/(cnt-1);
else x.delta=0;return x;
}
int fndl(int a,int b,int x){
int l=0,r=pos[a][b].size()-1;
if(r<0||pos[a][b][r]<x) return -1;
while(l<r){
int mid=(l+r)>>1;
if(pos[a][b][mid]<x) l=mid+1;
else r=mid;
}
return l;
}
int fndr(int a,int b,int x){
int l=0,r=pos[a][b].size()-1;
if(r<0||pos[a][b][0]>x) return -1;
while(l<r){
int mid=(l+r+1)>>1;
if(pos[a][b][mid]<=x) l=mid;
else r=mid-1;
}
return l;
}
int workans(int k,int l1,int r1,int l2,int r2){
int L=(1<<k),ps1=k1[k][l1],ps2=k1[k][r2-L+1];
int f1=fndl(k,ps1,l2);
int f3=fndl(k,ps2,l1);
if(f1==-1||f3==-1) return 0;
int f2=fndr(k,ps1,r2-L+1);
int f4=fndr(k,ps2,r1+1-L);
if(f2==-1||f4==-1||f1>f2||f3>f4) return 0;
Seq x,y;
if(f1==f2) x.st=x.ed=pos[k][ps1][f1],x.delta=1,x.sum=1;
else{
x.st=pos[k][ps1][f1],x.ed=pos[k][ps1][f2],
x.delta=pos[k][ps1][f1+1]-x.st,x.sum=(x.ed-x.st)/x.delta+1;
}
if(f3==f4) y.st=y.ed=pos[k][ps2][f3],y.delta=1,y.sum=1;
else{
y.st=pos[k][ps2][f3],y.ed=pos[k][ps2][f4],
y.delta=pos[k][ps2][f3+1]-y.st,y.sum=(y.ed-y.st)/y.delta+1;
}
Seq z=y;y.st=(l1+r2-z.ed-L+1),y.ed=(l1+r2-z.st-L+1);
x=Merge(x,y);
if(x.sum) return r2-x.st+1;
return 0;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1),m=26;
for(int i=1;i<=n;i++) ++bu[s[i]-'a'+1],k1[0][i]=s[i]-'a'+1;
for(int i=1;i<=m;i++) bu[i]+=bu[i-1];
for(int i=1;i<=n;i++) sa[bu[s[i]-'a'+1]]=i,--bu[s[i]-'a'+1];
for(int i=1;i<=n;i++) pos[0][k1[0][i]].push_back(i);
for(int k=1;k<=n;k<<=1){
int lgk=__lg(k)+1;
cnt=0;
for(int i=n-k+1;i<=n;i++) p[++cnt]=i;
for(int i=1;i<=n;i++){
if(sa[i]>k) p[++cnt]=sa[i]-k;
}
memset(bu,0,sizeof(bu));
for(int i=1;i<=n;i++) ++bu[k1[lgk-1][i]];
for(int i=1;i<=m;i++) bu[i]+=bu[i-1];
for(int i=n;i>=1;i--) sa[bu[k1[lgk-1][p[i]]]]=p[i],--bu[k1[lgk-1][p[i]]];
k1[lgk][sa[1]]=1,m=1;
for(int i=2;i<=n;i++){
if(k1[lgk-1][sa[i]]==k1[lgk-1][sa[i-1]]&&k1[lgk-1][sa[i]+k]==k1[lgk-1][sa[i-1]+k]) k1[lgk][sa[i]]=m;
else k1[lgk][sa[i]]=++m;
}
for(int i=1;i<=n;i++) pos[lgk][k1[lgk][i]].push_back(i);
}
scanf("%d",&Q);
for(int i=1;i<=Q;++i){
scanf("%d%d",&l,&r);ans=0;
for(int k=__lg(r-l)+1;k;--k){
ans=workans(k-1,l,min(l+(1<<k)-1,r-1),max(l+1,r-(1<<k)+1),r);
if(ans){
printf("%d\n",ans);
break;
}
}
if(ans==0) printf("%d\n",ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效