luogu P4094 [HEOI2016/TJOI2016]字符串
题面传送门
首先\([a,b]\)区间内匹配的子串的右端点一定要么\([c,d]\)区间无法匹配,要么右端点为\(b\)。
考虑二分答案,设当前要检查的答案为\(mid\),则待匹配的左端点肯定是\([a,b-mid+1]\)与\([c,c+mid-1]\)作匹配。
建出\(S\)串的SAM,则可以向上倍增找到\([c,c+mid-1]\)对应的parent树上的节点后,\([a,b-mid+1]\)能与其匹配的条件是在这个节点的子树中。将parent树拍成DFS序后建立主席树查询即可。时间复杂度\(O(n\log^2n)\)
但是常数很大,大概要跑\(2.2s\),其中瓶颈在主席树。考虑整体二分后离线树状数组,常数小了很多。大概跑了\(700ms\),就可以过了。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N 200000
#define M N*N+5
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
using namespace std;
int n,m,k,Bg[N+5],En[N+5],Ns,H,Ans[N+5],A[N+5],B[N+5],C[N+5],D[N+5],l[N+5],r[N+5],mid[N+5],Id[N+5],Fl;char S[N+5];
struct yyy{int to,z;};struct ljb{int head,h[N+5];yyy f[N+5];I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}}s;
namespace SAM{
int fa[N+5][18],d[N+5],lg[N+5],son[N+5][26],Lk[N+5],Le[N+5],p,q,La=1,cnt=1;I void Ins(int c){
Le[++cnt]=Le[p=La]+1;La=cnt;while(p&&!son[p][c]) son[p][c]=La,p=Lk[p];if(!p){Lk[La]=1;return;}
q=son[p][c];if(Le[q]==Le[p]+1){Lk[La]=q;return;}Le[++cnt]=Le[p]+1;Mc(son[cnt],son[q]);Lk[cnt]=Lk[q];Lk[q]=Lk[La]=cnt;while(p&&son[p][c]==q) son[p][c]=cnt,p=Lk[p];
}I void dfs(int x,int La){Bg[x]=++H;d[x]=d[La]+1;fa[x][0]=La;yyy tmp;RI i;for(i=1;fa[x][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1];for(i=s.h[x];i;i=tmp.z) tmp=s.f[i],dfs(tmp.to,x);En[x]=H;}
I void BD(){for(RI i=2;i<=cnt;i++) s.add(Lk[i],i),lg[i]=lg[i/2]+1;dfs(1,0);}I int Jp(int x,int w){for(RI i=lg[d[x]];~i;i--) Le[fa[x][i]]>=w&&(x=fa[x][i]);return x;}
}
namespace Tree{
int F[N+5];I void Ins(int x){while(x<=H) F[x]++,x+=x&-x;}I int Find(int x){int Ans=0;while(x) Ans+=F[x],x-=x&-x;return Ans;}
}
struct Ques{int x,y,id,Fl;};vector<Ques> Q[N+5];
int main(){
//freopen("1.in","r",stdin);freopen("1.out","w",stdout);
RI i,j;scanf("%d%d%s",&n,&m,S+1);for(i=1;i<=n;i++) SAM::Ins(S[i]-'a'),Id[i]=SAM::La;SAM::BD();
for(i=1;i<=m;i++)scanf("%d%d%d%d",&A[i],&B[i],&C[i],&D[i]),l[i]=0,r[i]=min(B[i]-A[i]+1,D[i]-C[i]+1)+1;while(1){
Fl=0;for(i=1;i<=m;i++) l[i]+1<r[i]&&(mid[i]=l[i]+r[i]>>1,Fl=1);if(!Fl) break;for(i=1;i<=n;i++)Q[i].clear();
for(i=1;i<=m;i++) l[i]+1<r[i]&&(Ns=SAM::Jp(Id[C[i]+mid[i]-1],mid[i]),Q[B[i]].push_back((Ques){Bg[Ns],En[Ns],i,1}),Q[A[i]+mid[i]-2].push_back((Ques){Bg[Ns],En[Ns],i,-1}),Ans[i]=0);
for(i=1;i<=n;i++)for(Tree::Ins(Bg[Id[i]]),j=0;j<Q[i].size();j++)Ans[Q[i][j].id]+=Q[i][j].Fl*(Tree::Find(Q[i][j].y)-Tree::Find(Q[i][j].x-1));for(i=1;i<=m;i++) l[i]+1<r[i]&&((Ans[i]?l[i]:r[i])=mid[i]);
}for(i=1;i<=m;i++) printf("%d\n",l[i]);
}