LOJ6031 字符串
字符串
令\(s\)与\(w\)为两字符串,定义:
- \(w[l, r]\)表示字符串\(w\)在区间\([l, r]\)中的子串;
- \(w\)在\(s\)中出现的频率定义为\(w\)在\(s\)中出现的次数;
- \(f(s, w, l, r)\)表示\(w[l, r]\)在\(s\)中出现的频率。
现在给定串\(s\),\(m\)个区间\([l, r]\)和长度\(k\),你要回答\(q\)个询问,每个询问给你一个长度为\(k\)的字符串\(w\)和两个整数\(a, b\),求:
\[\sum\limits_{i = a} ^ b f(s, w, l_i, r_i)
\]
对于\(100\%\)的数据,满足\(n, m, k, q \leq 10 ^ 5, \sum w \leq 10 ^ 5\),字符串由小写英文字母构成。
题解
SAM + 根号分治的奇怪组合。
http://jklover.hs-blog.cf/2020/04/16/Loj-6031-字符串/
注意到这题不好做的地方在于:虽然串长有保证,但是每次的询问可能有很多。
-
\(k\leq q\)时,暴力枚举子串。
可以将\([l,r]\)在\(m\)个区间中出现的所有位置存在vector中,二分找出\(a,b\)的位置,做差即为所求。
-
\(k>q\)时,暴力枚举询问。
就用经典的SAM定位法+倍增找祖先即可。
时间复杂度\(O(q(k+m\log n))=O(p+m\sqrt p\log n)\)。
CO int N=2e5+10;
int last=1,tot=1;
array<int,26> ch[N];
int fa[N],len[N],siz[N];
void extend(int c){
int x=last,cur=last=++tot;
len[cur]=len[x]+1,siz[cur]=1;
for(;x and !ch[x][c];x=fa[x]) ch[x][c]=cur;
if(!x) {fa[cur]=1; return;}
int y=ch[x][c];
if(len[y]==len[x]+1) {fa[cur]=y; return;}
int clone=++tot;
ch[clone]=ch[y],fa[clone]=fa[y],len[clone]=len[x]+1;
fa[cur]=fa[y]=clone;
for(;ch[x][c]==y;x=fa[x]) ch[x][c]=clone;
}
int cnt[N],ord[N];
void toposort(){
for(int i=1;i<=tot;++i) ++cnt[len[i]];
for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1];
for(int i=1;i<=tot;++i) ord[cnt[len[i]]--]=i;
for(int i=tot;i>=2;--i) siz[fa[ord[i]]]+=siz[ord[i]];
}
char s[N],w[N];
int n,m,q,k,L[N],R[N];
namespace Task1{
CO int K=320;
vector<int> vec[K][K];
IN int calc(CO vector<int>&vec,int a,int b){
return upper_bound(vec.begin(),vec.end(),b)-lower_bound(vec.begin(),vec.end(),a);
}
void main(){
for(int i=1;i<=m;++i)
if(1<=L[i] and L[i]<=R[i] and R[i]<=k)
vec[L[i]][R[i]].push_back(i);
while(q--){
scanf("%s",w+1);
int a=read<int>()+1,b=read<int>()+1;
int64 ans=0;
for(int l=1;l<=k;++l){
int x=1;
for(int r=l;r<=k;++r){
x=ch[x][w[r]-'a'];
if(!x) break;
if(vec[l][r].empty()) continue;
if(vec[l][r].back()<a or vec[l][r].front()>b) continue;
ans+=(int64)siz[x]*calc(vec[l][r],a,b);
}
}
printf("%lld\n",ans);
}
}
}
namespace Task2{
int anc[N][18];
vector<int> vec[N];
int query(int x,int lim){
if(len[x]<lim) return 0;
for(int i=17;i>=0;--i)
if(len[anc[x][i]]>=lim) x=anc[x][i];
return siz[x];
}
void main(){
for(int i=2;i<=tot;++i){
int x=ord[i];
anc[x][0]=fa[x];
for(int j=1;j<=17;++j) anc[x][j]=anc[anc[x][j-1]][j-1];
}
while(q--){
scanf("%s",w+1);
int a=read<int>()+1,b=read<int>()+1;
for(int i=a;i<=b;++i) vec[R[i]].push_back(L[i]);
int64 ans=0;
int x=1,l=0;
for(int r=1;r<=k;++r){
int c=w[r]-'a';
if(ch[x][c]) x=ch[x][c],++l;
else{
while(x and !ch[x][c]) x=fa[x];
if(!x) x=1,l=0;
else l=len[x]+1,x=ch[x][c]; // important
}
for(int i:vec[r])
if(l>=r-i+1) ans+=query(x,r-i+1);
}
printf("%lld\n",ans);
for(int i=a;i<=b;++i) vec[R[i]].clear();
}
}
}
int main(){
read(n),read(m),read(q),read(k);
scanf("%s",s+1);
for(int i=1;i<=n;++i) extend(s[i]-'a');
toposort();
for(int i=1;i<=m;++i) L[i]=read<int>()+1,R[i]=read<int>()+1;
if(k<=q) Task1::main();
else Task2::main();
return 0;
}
静渊以有谋,疏通而知事。