BZOJ3230 相似子串 后缀自动机做法

来源:互联网 发布:vue.js配合什么ui框架 编辑:程序博客网 时间:2024/06/11 12:33

……作为一名坚定的后缀自动机党,我当然要用后缀自动机做这题-_-

这个……我发现我写题解的时候非常语无伦次,主要原因是后缀自动机那些术语除了一个max我都忘了,所以为了方便说话我们定义一些东西

我们知道每个节点上(你理解为边上当然也可以的)有一个字符串,那么我们把这个字符串叫做a[x],这个字符串的长度叫做max[x],然后从根到一个点的路径能代表max[x]个字符串,我们把这些字符串叫做b[x]

只要我们能知道第i名的子串的端点和长度,那么我们就只需要对正反串分别建后缀自动机,然后通过后缀树上求LCA来求后(前)缀的LCP的长度,再和子串长度取min即可得出答案,所以关键就是如何求出排名为i的子串的端点和长度

我们可以通过对反串建后缀自动机搞出后缀树,但是光有后缀树还不够,我们需要知道后缀树上每个点(当然也可以理解为边)在原串里一个可能的位置P[x](我们把这个位置定义为最长的b[x]的结尾点可能的位置),以及每个点的所有儿子节点之间的字典序关系

容易知道所有叶子节点的位置都是n,那么对于一个点,如果他的位置可以是P[x],那么P[fa[x]]就可以是P[x]-(max[x]-max[fa[x]]),那么这样可以O(n)求出每个点的P

由后缀树的定义可知一个点的所有儿子代表的a的第一个字符都是不一样的,所以儿子之间的字典序就相当于每个儿子代表的a的第一个字符之间的大小关系,因为已经知道位置和max了,所以第一个字符也就相当于知道了

我们知道所有b就代表了所有子串,并且一个点的b的排名是一段连续区间,且所有点的b之间没有交,那么我们按照字典序dfs后缀树,然后就能知道每个点的b的排名区间,然后可以通过二分找到排名为i的子串在哪个点的b里,然后因为我们已经知道了这个点的P,以及这个点的b的区间,以及max,所以这个子串的端点和长度就都可求了

然后就可以做了

额,顶上看不得就看代码吧-_-

代码写的有点丑-_-

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<vector>#include<map>#include<set>#include<bitset>#include<queue>#include<stack>using namespace std;#define MAXN 200010#define MAXM 1010#define INF 1000000000#define MOD 1000000007#define eps 1e-8#define ll long longstruct vec{int to;int fro;};struct sam{int fa[MAXN],son[MAXN][26],mx[MAXN];int rt,lst,tot;sam(){lst=rt=tot=1;}void ins(int x);void pre1();void pre2();};struct G{vec mp[MAXN];int tai[MAXN],cnt;int fa[MAXN][20];int dep[MAXN];int dis[MAXN];inline void be(int x,int y);void dfs1(int x);void dfs2(int x);int lca(int x,int y);};sam am1,am2;G g1,g2;int tr[MAXN][26];char s[MAXN];int p1[MAXN],p2[MAXN],P[MAXN];int v[MAXN],t[MAXN];int n,m;vec mp[MAXN];int tai[MAXN],cnt;ll c[MAXN];int TOT;inline void G::be(int x,int y){mp[++cnt].to=y;mp[cnt].fro=tai[x];tai[x]=cnt;}void sam::ins(int x){int p=lst,np=++tot;mx[np]=mx[p]+1;while(p&&!son[p][x]){son[p][x]=np;p=fa[p];}if(!p){fa[np]=rt;}else{int q=son[p][x];if(mx[q]==mx[p]+1){fa[np]=q;}else{int nq=++tot;mx[nq]=mx[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));fa[nq]=fa[q];fa[np]=fa[q]=nq;while(p&&son[p][x]==q){son[p][x]=nq;p=fa[p];}}}lst=np;}void sam::pre1(){int i,j;for(i=1;i<=tot;i++){v[mx[i]]++;}for(i=1;i<=tot;i++){v[i]+=v[i-1];}for(i=tot;i;i--){t[v[mx[i]]--]=i;}for(i=tot;i!=1;i--){P[fa[t[i]]]=P[t[i]]-(mx[t[i]]-mx[fa[t[i]]]);tr[fa[t[i]]][s[P[t[i]]-(mx[t[i]]-mx[fa[t[i]]])+1]-'a']=t[i];}for(i=1;i<=tot;i++){for(j=25;~j;j--){if(tr[i][j]){g1.be(i,tr[i][j]);}}}}void sam::pre2(){int i;for(i=2;i<=tot;i++){g2.be(fa[i],i);}}void G::dfs1(int x){int i,j,t,y;TOT++;c[TOT]=c[TOT-1]+am1.mx[x]-am1.mx[am1.fa[x]];::t[TOT]=x;dep[x]=dep[fa[x][1]]+1;dis[x]=dis[fa[x][1]]+am1.mx[x]-am1.mx[am1.fa[x]];for(i=tai[x];i;i=mp[i].fro){y=mp[i].to;for(j=1,t=x;t;j++){fa[y][j]=t;t=fa[t][j];}dfs1(y);}}void G::dfs2(int x){int i,j,t,y;dep[x]=dep[fa[x][1]]+1;dis[x]=dis[fa[x][1]]+am2.mx[x]-am2.mx[am2.fa[x]];for(i=tai[x];i;i=mp[i].fro){y=mp[i].to;for(j=1,t=x;t;j++){fa[y][j]=t;t=fa[t][j];}dfs2(y);}}int G::lca(int x,int y){int i;if(dep[x]<dep[y]){swap(x,y);}for(i=19;i;i--){if(dep[fa[x][i]]>=dep[y]){x=fa[x][i];}}for(i=19;i;i--){if(fa[x][i]!=fa[y][i]){x=fa[x][i];y=fa[y][i];}}return x==y?dis[x]:dis[fa[x][1]];}int find(ll x){int l=1,r=TOT;int re;while(l<=r){int mid=l+r>>1;if(c[mid]>=x){re=mid;r=mid-1;}else{l=mid+1;}}return re;}ll sqr(ll x){return x*x;}int main(){int i;ll x,y;scanf("%d%d%s",&n,&m,s+1);for(i=n;i;i--){am1.ins(s[i]-'a');p1[i]=am1.lst;P[am1.lst]=n;}for(i=1;i<=n;i++){am2.ins(s[i]-'a');p2[i]=am2.lst;}am1.pre1();g1.dfs1(1);am2.pre2();g2.dfs2(1);while(m--){scanf("%lld%lld",&x,&y);if(x>c[TOT]||y>c[TOT]){printf("-1\n");continue ;}int xx=find(x),yy=find(y);int len1=am1.mx[t[xx]]-(c[xx]-x);int len2=am1.mx[t[yy]]-(c[yy]-y);int xxx=p2[P[t[xx]]-(c[xx]-x)];int yyy=p2[P[t[yy]]-(c[yy]-y)];printf("%lld\n",sqr(min(g1.lca(t[xx],t[yy]),min(len1,len2)))+sqr(min(g2.lca(xxx,yyy),min(len1,len2))));}return 0;}/*5 3ababa3 55 98 10*/



0 0