子串周期查询问题学习笔记
123asdfghjkl · · 个人记录
子串周期查询问题学习笔记
1.约定
证明 :不妨假设
同理可得,
若
若
可得
引理2 若串
证明:若 S 在 T 中出现了一次或两次,则引理已证完,因此只需考虑 S 在 T 中至少出现了 3 次。
对于两次出现位置
然后你就可以看图理解一下。
推论1 :
证明 :考虑其中最大的元素
推论2:对于一个串
证明:上述 x 组成的集合恰好为
根据推论2,我们有个大致的想法:
回到原问题,若求一个子串
引理4:
对于
有了这个引理,我们先求出
然后
考虑怎么求出
由引理1可知,我们只需要求出等差数列即可,我们可以只求出第一次、第二次和最后一次出现位置,就能求出
具体实现:
实现
具体地,假设母串是
为了实现 succ 操作,我们可以把所有长度为
4.2 等差数列求交及优化
对于一般问题,若求
但是字符串有特殊的性质,让我们可以
引理5:若四个字符串满足
这直接说明了:若
证明:
用反证法,假设
根据引理 2,可知
此时可以发现,
而当
若
如果实在看不懂这个证明,你可以直接取这种情况的特殊情况,及即
4.3 succ 和 pred 查询的优化
我们已经把等差数列求交优化成
我们不想二分,怎么办呢?
考虑对于每个本质不同的长度为
关于找等差数列,就直接在用
4.4 完结撒花
5.基本子串字典的扩展与应用
因为找不到原题,而且懒得自己造数据,我觉得应该放一下基本子串字典的板子题来熟悉代码实现。
例1
给定串
解,若
现在假设
update 我找到了 /kel 真.板子 https://acm.nflsoj.com/problem/338
#include<bits/stdc++.h>
#define N 400010
using namespace std;
char c[N];
int lg2[N],sa[N],oldrk[N*2],rk[N],name[21][N],cnt[N],pk[N],id[N],n,m;
vector<int>v[21][200001];
char obuf[1<<23],*O=obuf;
#define putchar(c) (*O++=c)
inline int read(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+int(ch-48);
ch=getchar();
}
return x;
}
void fw(int x){
if(x>=10)fw(x/10);
putchar(int(x%10)+48);
}
void init(){
scanf("%s",c+1);
n=strlen(c+1),m=27;
lg2[0]=-1;
for(int i=1;i<=n;i++)lg2[i]=lg2[i/2]+1;
int maxx=lg2[n]+1;
for(int i=1;i<=n;i++)cnt[name[0][i]=rk[i]=int(c[i]-'a'+1)]++;
for(int i=2;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[rk[i]]--]=i;
for(int w=1,now=1;w<n;w<<=1,now++){
int p=0;
for(int i=n-w+1;i<=n;i++)id[++p]=i;
for(int i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)cnt[pk[i]=rk[id[i]]]++;
for(int i=2;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[pk[i]]--]=id[i];
p=0;
memcpy(oldrk,rk,sizeof(rk));
for(int i=1;i<=n;i++){
if(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w])p++;
rk[sa[i]]=p;
}
m=p;
for(int i=1;i<=n;i++)if(sa[i]+w-1<=n)name[now][sa[i]]=rk[sa[i]];
}
for(int now=0;now<=maxx;now++){
for(int j=1;j<=n-(1<<now)+1;j++)v[now][name[now][j]].push_back(j);
}
}//name
struct jyytxdy{
int s,t,d;
jyytxdy(int s_=-1,int t_=-1,int d_=-1){
s=s_,t=t_,d=d_;
}
}o,res[N];
int find(jyytxdy a,int x){
return a.s<=x&&a.t>=x&&((x-a.s)%a.d==0);
}
jyytxdy merge(jyytxdy a,jyytxdy b){
if(a.d!=b.d){
if(a.s+a.d>=a.t)swap(a,b);
if(!find(a,b.s)){
if(!find(a,b.t))return o;
else return jyytxdy(b.t,b.t,1);
}
if(find(a,b.t))return b;
return jyytxdy(b.s,b.s,1);
}
if(a.s>b.t||a.t<b.s||(a.s-b.s)%a.d)return o;
return jyytxdy(max(a.s,b.s),min(a.t,b.t),a.d);
}
jyytxdy match(int l,int r,int d,int p){//在[l,r]中找长度为2^d,p开头的起始位置
if(l>r)return o;
int rank=name[d][p];
int s1=lower_bound(v[d][rank].begin(),v[d][rank].end(),l)-v[d][rank].begin();
if(s1==(int)v[d][rank].size()||v[d][rank][s1]>r)return o;
int s2=s1+1;
if(s2==(int)v[d][rank].size()||v[d][rank][s2]>r)return jyytxdy(v[d][rank][s1],v[d][rank][s1],1);
int D=v[d][rank][s2]-v[d][rank][s1];
int t=upper_bound(v[d][rank].begin(),v[d][rank].end(),r)-v[d][rank].begin()-1;
return jyytxdy(v[d][rank][s1],v[d][rank][t],D);
}
void solve(){
int l=read()+1,r=read()+1;
int tot=0;
for(int now=0,w=1;w<=r-l;now++,w<<=1){
jyytxdy a=match(l,min(r-w+1,l+w)-1,now,r-w+1);//[l,l+2*w-1],右区间+1是为了不取到长度为2*w的bored
if(a.d<0)continue;
jyytxdy b=match(max(l,r-2*w+1)+1,r-w+1,now,l);//[r-2*w+1,r]
if(b.d<0)continue;
a.s=a.s+w-1-(l-1),a.t=a.t+w-1-(l-1);//把位置转化为长度
b.s=r-b.s+1,b.t=r-b.t+1,swap(b.s,b.t);
a=merge(a,b);
if(a.d>=0) res[++tot]=a;
}
if(!tot){
putchar('-');
putchar('1');
putchar('\n');
return;
}
int minn=res[1].s,maxx=res[tot].t,sum=0;
for(int i=1;i<=tot;i++)sum+=(res[i].t-res[i].s)/res[i].d+1;
fw(minn),putchar(' '),fw(maxx),putchar(' '),fw(sum),putchar('\n');
}
int T;
int main(){
init();
cin>>T;
while(T--)solve();
fwrite(obuf,O-obuf,1,stdout);
}