「HAOI2018」字串覆盖
「HAOI2018」字串覆盖
这自然有后缀数组和后缀自动的写法,我写的是后缀数组
现对于\(A,B\)两串拼接后建立\(\text{SA}\)
对于查询的四个参数\([s,t,l,r]\),在\(\text{SA}\)上找到能够匹配\([l,r]\)的\(\text{rank}\)区间\([l',r']\)
这个\([l',r']\)就用\(\text{SA}\)的\(\text{height}\)数组上倍增即可\(O(\log n)\)找到
由于\(K>n\),显然覆盖就是从左到右依次匹配每个\(\text{rank}\)在\([l',r']\)中的\(i\),能放就放
数据范围提示我们切分写
Part1 \(r-l\leq 50\)
对于每种不同的\(r-l\),倍增预处理
我们将依次匹配的过程描述成一个个跳跃
对于每个\(i\)找到后面第一个\(j\)满足\(j>i+r-l,\text{LCP}(i,j)\ge r-l+1\)
具体的,将\(\text{SA}\)的\(\text{height}\)数组按照\(\text{height}_p\ge r-l+1\)分成一段一段
每个连续段中的两个位置\(\text{LCP}\ge r-l+1\)
合法的\(i,j\)一定出现的某个连续段中
找到每一个这样一个连续段,然后双指针得到合法的\(i,j\)即可
这样的跳跃关系,以及跳跃过程中的答案,可以通过倍增来维护出来
对于每个询问,可以先找到区间内第一个合法的点\(i_0\),然后倍增查询答案即可
找到\(i_0\)可以用主席树二分出\(\text{rank}\)在\([l',r']\)内的第一个\(i_0\ge s\)的位置
复杂度为\(O(50n\log n+q\log n)\)
Part2 \(r-l>50\)
根据数据范围,这里我们只要能够暴力跳每一个合法的\(i\)即可
那么像上面一样,用主席树每次找\([l',r']\)内第一个\(i'> i+r-l\)
每次\(\log n\)跳即可,复杂度为\(O(\frac{nq}{r-l}\log n)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) f|=IO=='-';
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}
const int N=2e5+10,M=N*18,S=60;
int n,K,q;
char s[N];
int cnt[N],rk[N<<1],tmp[N],sa[N];
int st[19][N],Log[N];
void Build() {
rep(i,1,n) cnt[(int)s[i]]++;
rep(i,1,200) cnt[i]+=cnt[i-1];
drep(i,n,1) sa[cnt[(int)s[i]]--]=i;
rep(i,1,n) rk[sa[i]]=rk[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
for(int m=rk[sa[n]],k=1;k<n && m<n;k<<=1,m=rk[sa[n]]) {
int h=0;
rep(i,n-k+1,n) tmp[++h]=i;
rep(i,1,n) if(sa[i]>k) tmp[++h]=sa[i]-k;
memset(cnt,0,(m+1)<<2);
rep(i,1,n) cnt[rk[i]]++;
partial_sum(cnt+1,cnt+m+1,cnt+1);
drep(i,n,1) sa[cnt[rk[tmp[i]]]--]=tmp[i];
rep(i,1,n) tmp[sa[i]]=tmp[sa[i-1]]+(rk[sa[i]]!=rk[sa[i-1]] || rk[sa[i]+k]!=rk[sa[i-1]+k]);
memcpy(rk,tmp,(n+1)<<2);
}
rep(i,2,n) Log[i]=Log[i>>1]+1;
int h=0;
rep(i,1,n) {
int j=sa[rk[i]-1];
if(h) h--;
while(s[i+h]==s[j+h]) h++;
st[0][rk[i]-1]=h;
}
rep(i,1,Log[n]) {
int len=1<<(i-1);
rep(j,1,n-len+1) st[i][j]=min(st[i-1][j],st[i-1][j+len]);
}
}
int ls[M],rs[M],c[M],rt[N],tcnt;
void Upd(int &p,int pre,int l,int r,int x){
c[p=++tcnt]=c[pre]+1,ls[p]=ls[pre],rs[p]=rs[pre];
if(l==r) return;
int mid=(l+r)>>1;
x<=mid?Upd(ls[p],ls[pre],l,mid,x):Upd(rs[p],rs[pre],mid+1,r,x);
}
int Que(int p1,int p2,int l,int r,int x){
if(c[p1]==c[p2] || r<x) return n+1;
if(l==r) return l;
int mid=(l+r)>>1,t=Que(ls[p1],ls[p2],l,mid,x);
return t<=n?t:Que(rs[p1],rs[p2],mid+1,r,x);
}
vector <int> G[S];
int A[N],B[N],L[N],R[N],X[N];
ll ans[N];
ll H[20][N]; int F[20][N];
int D[N],C;
int main(){
n=rd(),K=rd();
scanf("%s",s+1),scanf("%s",s+n+1);
n*=2,Build(),n/=2;
rep(i,1,n*2) {
rt[i]=rt[i-1];
if(sa[i]<=n) Upd(rt[i],rt[i],1,n,sa[i]);
}
rep(i,1,q=rd()) {
A[i]=rd(),B[i]=rd();
int l=rd(),x=rd()-l+1,r=l=rk[l+n];
drep(j,18,0) {
if(r+(1<<j)<=n*2 && st[j][r]>=x) r+=1<<j;
if(l>(1<<j) && st[j][l-(1<<j)]>=x) l-=1<<j;
}
L[i]=l,R[i]=r,X[i]=x;
if(n<=5000 && q<=5000) {
int p=A[i],e=B[i]-x+1;
while(p<=e) {
while(p<=e && (rk[p]<l || rk[p]>r)) p++;
if(p>e) break;
ans[i]+=K-p,p+=x;
}
} else if(X[i]>=S) {
int p=A[i],e=B[i]-x+1;
while(p<=e) {
int c=0;
while(++c<5 && p<=e && (rk[p]<l || rk[p]>r)) p++;
if(p>e) break;
if(rk[p]<l || rk[p]>r) p=Que(rt[l-1],rt[r],1,n,p);
if(p>e) break;
ans[i]+=K-p,p+=x;
}
} else G[X[i]].pb(i);
}
rep(x,1,S-1) if(G[x].size()) {
rep(i,1,n) F[0][i]=n+1;
rep(i,0,17) F[i][n+1]=n+1;
rep(i,1,n*2) {
int j=i;
while(j<n*2 && st[0][j]>=x) j++;
C=0;
rep(k,i,j) if(sa[k]<=n) D[++C]=sa[k];
if(C) {
sort(D+1,D+C+1);
int j=1;
rep(i,1,C) {
while(j<=C && D[j]-D[i]<x) j++;
if(j<=C) F[0][D[i]]=D[j];
}
}
i=j;
}
rep(i,1,n) H[0][i]=K-i;
rep(j,1,17) rep(i,1,n) {
F[j][i]=F[j-1][F[j-1][i]];
H[j][i]=H[j-1][i]+H[j-1][F[j-1][i]];
}
rep(d,0,G[x].size()-1) {
int i=G[x][d],e=B[i]-x+1;
int p=Que(rt[L[i]-1],rt[R[i]],1,n,A[i]);
if(p>e) continue;
drep(j,17,0) if(F[j][p]<=e) {
ans[i]+=H[j][p];
p=F[j][p];
}
ans[i]+=H[0][p];
}
}
rep(i,1,q) printf("%lld\n",ans[i]);
}