P4482 [BJWC2018] Border 的四种求法
题意
给定字符串 \(s\),\(q\) 次询问 \(s[l,r]\) 的 border。
\(n,q\le 2\times10^5\)
题意
这里介绍的是 SAM+树剖的双 log 做法。
假设 \(s[l,r]\) 的 border 是 \(s[l,x]\),则其一定满足 \(s[l,x]=s[r-x+l,r]\),从而得到 \(\text{LCS}(s[1,x],s[1,r])\ge x-l+1\)。
把 \(s\) 串建一个 SAM,根据 SAM 经典结论,两个串的 LCS 是 parent 树上的 LCA 的最长串。由于两个串都是前缀,其代表的点非常好找。如果 parent 树的最大深度足够小,那么我们可以枚举这个 LCA 然后 dfn 序转化成求区间 \(x\) 最大值。在一般情况下,这个做法显然平方,考虑对 parent 树树剖,转化为了 \(O(\log n)\) 段重链的前缀。下面的“重链末尾”均指的是查询的重链前缀的末端元素。
对于每条重链的非末尾元素,考虑将 \(O(n\log n)\) 个关于重链前缀的查询离线,将限制式子移项变成 \(x-\text{LCS}(s[1,x],s[1,r])+1\le l\),并令 \(b_x=x-\text{LCS}(s[1,x],s[1,r])\),开一棵线段树维护 \(b_i\) 的最大值,而由于每个 \(x\) 只会插入 \(O(\log n)\) 次(因为只有轻边才会造成一次插入),所以暴力插入复杂度正确。查询只需要在 \([l,r]\) 范围内线段树二分找到最大的 \(i\) 满足 \(b_i<l\) 即可。
对于每条重链的前缀末尾元素,考虑轻重儿子分开考虑,先将轻儿子的贡献加进去,然后删掉那个连接重链末尾的轻儿子,再统计贡献。对于重儿子,直接倒着枚举重链即可。都是只需要单点插入、单点删除。
然后还要处理一下两个点有祖先关系的答案,此时做祖先的点代表的前缀是那个点代表的等价类内的最长串(因为是前缀了,往前面添加字符必然导致 endpos 改变),所以以 \(len\) 作为 LCS 长度并无问题。实际上这种情况可以和前面几种情况放一块写,具体见代码。
总时间复杂度 \(O(n\log^2 n)\)。
代码中 q1,q2,q3 分别代表了重链前缀上和重链前缀的轻儿子的查询、重链末尾的轻儿子查询、重链末尾的重链查询,v1,v2,v3 分别代表了作为轻儿子的贡献、作为重链前缀上的点的贡献和作为重链末尾后面的元素的贡献。
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<array>
#include<tuple>
#include<ctime>
#include<random>
#include<cassert>
#include<chrono>
#define x1 xx1
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0)
#define OTIE cout.tie(0)
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define mp make_pair
#define fi first
#define se second
#define gc getchar
#define pc putchar
#define pb emplace_back
#define un using namespace
#define il inline
#define all(x) x.begin(),x.end()
#define mem(x,y) memset(x,y,sizeof x)
#define popc __builtin_popcountll
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) ((x)&-(x))
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
//#define double long double
//#define int long long
//#define int __int128
using namespace std;
using i64=long long;
using u64=unsigned long long;
using pii=pair<int,int>;
template<typename T1,typename T2>inline void ckmx(T1 &x,T2 y){x=x>y?x:y;}
template<typename T1,typename T2>inline void ckmn(T1 &x,T2 y){x=x<y?x:y;}
inline auto rd(){
int qwqx=0,qwqf=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')qwqf=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){qwqx=(qwqx<<1)+(qwqx<<3)+ch-48;ch=getchar();}return qwqx*qwqf;
}
template<typename T>inline void write(T qwqx,char ch='\n'){
if(qwqx<0){qwqx=-qwqx;putchar('-');}
int qwqy=0;static char qwqz[40];
while(qwqx||!qwqy){qwqz[qwqy++]=qwqx%10+48;qwqx/=10;}
while(qwqy--){putchar(qwqz[qwqy]);}if(ch)putchar(ch);
}
bool Mbg;
const int mod=998244353;
template<typename T1,typename T2>inline void adder(T1 &x,T2 y){x+=y,x=x>=mod?x-mod:x;}
template<typename T1,typename T2>inline void suber(T1 &x,T2 y){x-=y,x=x<0?x+mod:x;}
const int maxn=4e5+5,inf=0x3f3f3f3f;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n,Q;
struct query{
int l,r;
}q[maxn];
int ans[maxn];
char s[maxn];
vector<int>G[maxn];
int nxt[maxn][26],fa[maxn],len[maxn];
int cur,tot;
void ins(int ch){
int x=cur;cur=++tot,len[cur]=len[x]+1;
while(x&&!nxt[x][ch])nxt[x][ch]=cur,x=fa[x];
if(!x)return fa[cur]=1,void();
int y=nxt[x][ch];
if(len[y]==len[x]+1)return fa[cur]=y,void();
int z=++tot;
len[z]=len[x]+1,fa[z]=fa[y],fa[y]=fa[cur]=z,memcpy(nxt[z],nxt[y],sizeof nxt[y]);
while(x&&nxt[x][ch]==y)nxt[x][ch]=z,x=fa[x];
}
int dep[maxn],son[maxn],siz[maxn];
void dfs1(int x){
siz[x]=1;
for(int u:G[x]){
dep[u]=dep[x]+1,dfs1(u),siz[x]+=siz[u];
if(!son[x]||siz[u]>siz[son[x]])son[x]=u;
}
}
int tp[maxn];
//重链
vector<int>vec[maxn];
void dfs2(int x,int rt){
tp[x]=rt,vec[rt].pb(x);
if(son[x])dfs2(son[x],rt);
for(int u:G[x])if(u^son[x])dfs2(u,u);
}
//在点fa[i]的轻儿子i中插入前缀p
vector<pii>v[maxn],v3[maxn];
vector<int>v2[maxn];
/*
q1 lca 是重链前缀
q2 lca 是重链末尾
*/
vector<int>q1[maxn],q2[maxn],q3[maxn];
int pt[maxn];
namespace sgt{
int d[maxn<<2];
#define mid ((l+r)>>1)
il void pu(int p){
d[p]=min(d[lson(p)],d[rson(p)]);
}
void bd(int l=1,int r=n,int p=1){
d[p]=inf;
if(l==r)return;
bd(l,mid,lson(p)),bd(mid+1,r,rson(p));
}
void add(int x,int val,int l=1,int r=n,int p=1){
if(l==r)return d[p]=val,void();
x<=mid?add(x,val,l,mid,lson(p)):add(x,val,mid+1,r,rson(p));
pu(p);
}
void del(int x,int l=1,int r=n,int p=1){
if(l==r)return d[p]=inf,void();
x<=mid?del(x,l,mid,lson(p)):del(x,mid+1,r,rson(p));
pu(p);
}
vector<array<int,3>>arr;
void get(int ll,int rr,int l=1,int r=n,int p=1){
if(ll<=l&&r<=rr)return arr.push_back({l,r,p});
if(ll<=mid)get(ll,rr,l,mid,lson(p));
if(rr>mid)get(ll,rr,mid+1,r,rson(p));
}
int fnd(int v,int l,int r,int p){
if(l==r)return l;
if(d[rson(p)]<v)return fnd(v,mid+1,r,rson(p));
return fnd(v,l,mid,lson(p));
}
int qry(int l,int r,int v){
if(l>r)return -1;
arr.clear(),get(l,r);
reverse(all(arr));
for(auto i:arr)if(d[i[2]]<v)return fnd(v,i[0],i[1],i[2]);
return -1;
}
#undef mid
}un sgt;
inline void solve_the_problem(){
scanf("%s",s+1),n=strlen(s+1),Q=rd();
cur=tot=1,fa[1]=0,len[1]=0;
rep(i,1,n)ins(s[i]-'a');
int P=1;
rep(i,1,n)P=nxt[P][s[i]-'a'],pt[i]=P;
// rep(i,1,n)write(pt[i],32);P__;
rep(i,2,tot)G[fa[i]].pb(i);
// rep(i,1,tot)write(len[i],32);P__;
// rep(i,2,tot)write(fa[i],32),write(i);
dfs1(1),dfs2(1,1);
rep(i,1,Q){
int l=rd(),r=rd();
q[i]=(query){l,r};
int p=pt[r];
q1[p].pb(i),q3[p].pb(i);
while(tp[p]!=1){
const int t=tp[p],ft=fa[t];
q2[t].pb(i),q3[ft].pb(i);
if(ft!=tp[ft])q1[fa[ft]].pb(i);
p=ft;
}
}
rep(i,1,n){
int p=pt[i];
v2[p].pb(i),v3[p].pb(mp(i,i-len[p]));
while(tp[p]!=1){
const int t=tp[p],ft=fa[t];
v[t].pb(mp(i,i-len[ft])),p=ft;
}
}
bd();
//type 1
rep(r,1,tot)if(tp[r]==r){
for(int x:vec[r]){
for(int u:G[x])if(u^son[x]){
for(pii _:v[u]){
int i=_.fi,val=_.se;
add(i,val);
}
}
for(pii _:v3[x]){
int i=_.fi,val=_.se;
add(i,val);
}
for(int i:q1[x]){
int res=qry(q[i].l,q[i].r-1,q[i].l);
if(res!=-1)ckmx(ans[i],res-q[i].l+1);
}
}
for(int x:vec[r]){
for(int u:G[x])if(u^son[x]){
for(pii _:v[u]){
int i=_.fi;
del(i);
}
}
for(pii _:v3[x]){
int i=_.fi;
del(i);
}
}
}
// write(ans[1]);
//type 2
rep(r,1,tot)if(tp[r]==r){
reverse(all(vec[r]));
for(int x:vec[r]){
for(int u:G[x])if(u^son[x]){
for(pii _:v[u]){
int i=_.fi;
add(i,i);
}
}
for(int u:G[x])if(u^son[x]){
if(q2[u].empty())continue;
for(pii _:v[u]){
int i=_.fi;
del(i);
}
for(int i:q2[u]){
int res=qry(q[i].l,q[i].r-1,q[i].l+len[x]);
if(res!=-1)ckmx(ans[i],res-q[i].l+1);
}
for(pii _:v[u]){
int i=_.fi;
add(i,i);
}
}
}
for(int x:vec[r]){
for(int u:G[x])if(u^son[x]){
for(pii _:v[u]){
int i=_.fi;
del(i);
}
}
for(int i:v2[x]){
del(i);
}
}
}
//type 3
rep(r,1,tot)if(tp[r]==r){
for(int x:vec[r]){
for(int i:v2[x]){
add(i,i);
}
for(int i:q3[x]){
int res=qry(q[i].l,q[i].r-1,q[i].l+len[x]);
if(res!=-1)ckmx(ans[i],res-q[i].l+1);
}
}
for(int x:vec[r]){
for(int i:v2[x]){
del(i);
}
}
}
rep(i,1,Q)write(ans[i]);
}
bool Med;
signed main(){
// freopen(".in","r",stdin);freopen(".out","w",stdout);
fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);
int _=1;
while(_--)solve_the_problem();
}
/*
abbabbaa
1
1 8
*/

浙公网安备 33010602011771号