4556: [Tjoi2016&Heoi2016]字符串 后缀自动机 详细
来源:互联网 发布:暗黑2 1.13 完美 mac 编辑:程序博客网 时间:2024/06/16 16:09
4556: [Tjoi2016&Heoi2016]字符串
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 980 Solved: 384
[Submit][Status][Discuss]
Description
Input
Output
对于每一次询问,输出答案。
Sample Input
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4
Sample Output
1
2
2
2
这是一道后缀自动机的好题。。。。至少我觉得是,因为我很菜
但使用SA的人可以无视了。。
然后我最主要的东西是从这里学的:点这里
在这里我学会了很多东西:
1.有关于SAM parent树的理解与应用
2.有关线段树合并的小姿势
根据题解,我们可以先把字符串反过来,然后问题就转换为:给定两个子串a和b,求的a所有子串和b的最长公共后缀的最大值。
然后我们当然要先二分答案啦
那么如何判断呢?
我们可以现在parent树上进行倍增,就可以得到下图中(已将串翻转)ec这一段在SAM上的点,因为parent树上面的点都拥有共同的后缀嘛!
SAM的常用技巧!!!我才不会告诉你我之前理解不是十分透彻呢!
找到点后就好办啦,我们只需要看一下这个right集合中是否有在a,b这一段区间的就可以了啊
至于如何弄出right集合请看下文
接着自然就想到要知道一个点的right集合,注意,不是大小。因为一个点的right集合是他parent树上所有点的集合的并集(有点绕,请自己理解),所以这里可以用线段树合并可做,当然这里的线段树合并有一点点区别,因为一棵树要和多棵合并,然而之前的我做过的都只是上一棵(当然也有可能是我以前的模板太菜了)。
遇到的问题是见下图
(画的很丑,我也不知道是谁画的,所以不要怪我) 但要是你们在别的博客上看到这幅图,那他肯定是抄的
大意就是y是x和z在parent树上的父亲,然后他的right集合就是x和z合并起来,然而如果你直接搞,会发现到最后x多了一个右儿子,这肯定错了,因为当你访问到x时,你会发现他多了一个儿子的儿子=孙子
然后这怎么办呢?我想了很久,于是膜了一下代码,于是就涨姿势了。大概就是要新建点啊(这个想到了,于是我码了一个十分垃圾的东西,这个就不说了),每次对于y都新建一个,具体看代码
还有一点小细节是这题的线段树不需要维护任何东西,只需要知道有没有建立过某一段区间就好了,因为你只需要知道有没有嘛
然后这题大概就做完了吧。。还有什么不懂的可以看看代码(我的代码比较丑,求right的时候还是递归的,接着变量名可能不是常见的i,j,(⊙o⊙)…代码风格因人而异啦),当然也可以留言
时间复杂度O(n*log^2n)
至于空间的话,大概就是线段树的空间吧,略微有点大
似乎没有SA+主席树的优越,那我为什么要用SAM呢,因为我要加深理解啊!
#include<cstdio>#include<cstdlib>#include<cstring>const int N=100005*2;int n,m;char ss[N];struct qq{int son[26],pre,step;}s[N];int tot,Last;struct qr{int c,s1,s2;}tr[N*20];int num=0;int rt[N];int Pos[N];void change (int &now,int l,int r,int x){if (now==0) now=++num;tr[now].c++;if (l==r) return ;int mid=(l+r)>>1;if (x<=mid) change(tr[now].s1,l,mid,x);else change(tr[now].s2,mid+1,r,x);}void ins (int x,int pos){int p=Last,np=++tot;s[np].step=s[p].step+1;change(rt[np],1,n,pos);Pos[pos]=np;while (p!=0&&s[p].son[x]==0) s[p].son[x]=np,p=s[p].pre;if (p==0) s[np].pre=1;else{int q=s[p].son[x];if (s[q].step==s[p].step+1) s[np].pre=q;else{int nq=++tot;s[nq]=s[q];s[nq].step=s[p].step+1;s[np].pre=s[q].pre=nq;while (p!=0&&s[p].son[x]==q) s[p].son[x]=nq,p=s[p].pre;}}Last=np;}struct qt{int x,y,last;}e[N];int num1,last[N];void init (int x,int y){num1++;e[num1].x=x;e[num1].y=y;e[num1].last=last[x];last[x]=num1;return ;}int Merge (int x,int y)//注意啦 {if (!x||!y) return x+y;int z=++num;tr[z].s1=Merge(tr[x].s1,tr[y].s1);tr[z].s2=Merge(tr[x].s2,tr[y].s2);return z;}void dfs (int x){for (int u=last[x];u!=-1;u=e[u].last){int y=e[u].y;dfs(y);if (x==1) continue;rt[x]=Merge(rt[x],rt[y]);}return ;}int mymin (int x,int y){return x<y?x:y;}int get (int now,int L,int R,int l,int r){if (now==0) return 0;if (l==L&&r==R)return 1;int s1=tr[now].s1,s2=tr[now].s2;int mid=(L+R)>>1;if (r<=mid) return get(s1,L,mid,l,r);else if (l>mid) return get(s2,mid+1,R,l,r);else return get(s1,L,mid,l,mid)+get(s2,mid+1,R,mid+1,r);}int fa[N][20];void dfs1 (int x){fa[x][1]=s[x].pre;for (int u=2;;u++){if (fa[fa[x][u-1]][u-1]==0) break;fa[x][u]=fa[fa[x][u-1]][u-1];}for (int u=last[x];u!=-1;u=e[u].last)dfs1(e[u].y);}bool check (int x,int now,int l,int r)//这一个答案行不行 {for (int u=18;u>=1;u--)if (s[fa[now][u]].step>=x) now=fa[now][u];if (get(rt[now],1,n,l,r)>0) return true;return false;}int main(){scanf("%d%d",&n,&m);scanf("%s",ss+1);tot=Last=1;for (int u=n;u>=1;u--) ins(ss[u]-'a',n-u+1);num1=0;memset(last,-1,sizeof(last));for (int u=2;u<=tot;u++)init(s[u].pre,u);dfs(1);dfs1(1);while (m--){int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);a=n-a+1;b=n-b+1;c=n-c+1;d=n-d+1;int l=1,r=mymin(a-b+1,c-d+1);int ans=0;while (l<=r){int mid=(l+r)>>1;if (check(mid,Pos[c],b+mid-1,a)==true){ans=mid;l=mid+1;}else r=mid-1;}printf("%d\n",ans);}return 0;}
- 4556: [Tjoi2016&Heoi2016]字符串 后缀自动机 详细
- 4556: [Tjoi2016&Heoi2016]字符串(后缀自动机做法)
- 4556: [Tjoi2016&Heoi2016]字符串
- 4556: [Tjoi2016&Heoi2016]字符串
- 4556: [Tjoi2016&Heoi2016]字符串
- [后缀数组 主席树] BZOJ 4556 [Tjoi2016&Heoi2016]字符串
- BZOJ 4556: [Tjoi2016&Heoi2016]字符串 后缀数组 主席树
- BZOJ4556:[Tjoi2016&Heoi2016]字符串 (后缀自动机+树上倍增+二分答案+线段树合并)
- BZOJ 4556 [Tjoi2016&Heoi2016]字符串
- bzoj 4556 [Tjoi2016&Heoi2016]字符串
- bzoj 4556 [Tjoi2016&Heoi2016]字符串
- bzoj 4556 [Tjoi2016&Heoi2016]字符串
- [达成成就:Tjoi2016&Heoi2016全AC] bzoj 4556: [Tjoi2016&Heoi2016]字符串 后缀数组+可持久化线段树
- bzoj 4556 [Tjoi2016&Heoi2016]字符串 二分+后缀数组+主席树+RMQ
- BZOJ4556: [Tjoi2016&Heoi2016]字符串
- Bzoj4556: [Tjoi2016&Heoi2016]字符串
- BZOJ4556 [Tjoi2016&Heoi2016]字符串
- bzoj4556【TJOI2016&HEOI2016】字符串
- redhat 5.4/5.5/5.8/6.0/6.3 iso 镜像下载
- Netty生产级的心跳和重连机制
- 地图之CLLocationManager的使用
- php中关于线程thread的使用
- vue使用过程中遇到的错误提示一
- 4556: [Tjoi2016&Heoi2016]字符串 后缀自动机 详细
- SFP光模块大全,你想了解的这里都有!
- scala 九大排序算法
- Django-Cookie的使用
- 常见的视频流协议
- haproxy+keepalived配置
- 多边形划分-卡特兰数
- Merge into使用详解-
- Django-Cookie与装饰器