bzoj 4556 字符串

题目大意:

一个字符串 $m$次询问

每次询问$s[a:b]$的所有子串与$s[c:d]$的$lcp$最大值

思路:

因为有$lcp$ 考虑把串翻转转化为$lcs$

建立后缀树,如何在树上通过$s[c:d]$查找是否存在$s[a:b]$的一个子串

可以二分答案$x$,找到树上$d$点的最远的一个祖先满足其$maxlen \ge x$,通过倍增实现

在这个节点的所有$right$中若能找到$a+x-1$至$b$中的一个,则说明该答案可行

实现$right$集合可以使用线段树合并,因为是儿子的后缀所以可以继承

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<queue>
 8 #include<vector>
 9 #include<map>
10 #include<set>
11 #define ll long long
12 #define inf 2139062143
13 #define MAXN 200100
14 #define MOD 998244353
15 #define rep(i,s,t) for(register int i=(s),i##__end=(t);i<=i##__end;++i)
16 #define dwn(i,s,t) for(register int i=(s),i##__end=(t);i>=i##__end;--i)
17 #define ren for(register int i=fst[x];i;i=nxt[i])
18 #define pb(i,x) vec[i].push_back(x)
19 #define pls(a,b) (a+b)%MOD
20 #define mns(a,b) (a-b+MOD)%MOD
21 #define mul(a,b) (1LL*(a)*(b))%MOD
22 using namespace std;
23 inline int read()
24 {
25     int x=0,f=1;char ch=getchar();
26     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
27     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
28     return x*f;
29 }
30 int n,m,tot,las,tr[MAXN][26],mxl[MAXN],fa[MAXN],pos[MAXN];
31 char ch[MAXN];
32 int fst[MAXN],nxt[MAXN],to[MAXN],cnt,f[MAXN][20],dep[MAXN];
33 int rt[MAXN],num,ls[MAXN*20],rs[MAXN*20];
34 void add(int u,int v) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;}
35 int merge(int x,int y)
36 {
37     if(!(x*y)) return x|y;int z=++num;
38     ls[z]=merge(ls[x],ls[y]),rs[z]=merge(rs[x],rs[y]);
39     return z;
40 }
41 void mdf(int &k,int l,int r,int x)
42 {
43     if(!k) k=++num;if(l==r) return ;int mid=l+r>>1;
44     if(x<=mid) mdf(ls[k],l,mid,x);
45     if(x>mid) mdf(rs[k],mid+1,r,x);
46 }
47 int query(int k,int l,int r,int a,int b)
48 {
49     if(!k) return 0;if(l==a&&r==b) return 1;int mid=l+r>>1;
50     if(b<=mid) return query(ls[k],l,mid,a,b);
51     else if(a>mid) return query(rs[k],mid+1,r,a,b);
52     else return query(ls[k],l,mid,a,mid)|query(rs[k],mid+1,r,mid+1,b);
53 }
54 void extend(int c,int x)
55 {
56     int p=las,np=las=++tot;mxl[np]=mxl[p]+1;mdf(rt[np],1,n,x);
57     for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=np;
58     if(!p) {fa[np]=1;return ;}
59     int q=tr[p][c];if(mxl[q]==mxl[p]+1) {fa[np]=q;return ;}
60     int nq=++tot;mxl[nq]=mxl[p]+1;
61     memcpy(tr[nq],tr[q],sizeof(tr[nq])); 
62     fa[nq]=fa[q],fa[np]=fa[q]=nq;
63     for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq;
64 }
65 void dfs(int x,int pa)
66 {
67     dep[x]=dep[pa]+1,f[x][0]=pa;
68     rep(j,1,19) if((1<<j)<=dep[x]) f[x][j]=f[f[x][j-1]][j-1];else break;
69     ren dfs(to[i],x),rt[x]=merge(rt[x],rt[to[i]]);
70 }
71 int cheq(int x,int mx,int l,int r)
72 {
73     if(!mx) return 1;x=pos[x];
74     dwn(i,19,0) if((1<<i)<=dep[x]&&mxl[f[x][i]]>=mx) x=f[x][i];
75     return query(rt[x],1,n,l+mx-1,r);
76 }
77 int main()
78 {
79     n=read(),m=read();scanf("%s",ch+1);las=tot=1;int a,b,c,d,l,r,ans;
80     dwn(i,n,1) extend(ch[i]-'a',n-i+1),pos[n-i+1]=las;
81     rep(i,1,tot) add(fa[i],i);dfs(1,0);
82     while(m--)
83     {
84         a=n-read()+1,b=n+1-read(),c=n+1-read(),d=n+1-read();
85         swap(a,b);swap(c,d);l=0,r=min(b-a+1,d-c+1),ans=0;
86         for(int mid=l+r>>1;l<=r;mid=l+r>>1)
87             if(cheq(d,mid,a,b)) ans=mid,l=mid+1;
88             else r=mid-1;
89         printf("%d\n",ans);
90     }
91 }
View Code

(这种线段树必须要新开点,因为有可能一个点$a$变成了另一个点$b$的儿子,在$c$合并到$b$时,$a$也被合并了

 

posted @ 2019-03-11 12:53  jack_yyc  阅读(173)  评论(0编辑  收藏  举报