4556: [Tjoi2016&Heoi2016]字符串 后缀自动机 详细

4556: [Tjoi2016&Heoi2016]字符串

Time Limit: 20 Sec   Memory Limit: 128 MB
Submit: 980   Solved: 384
[ Submit][ Status][ Discuss]

Description

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
O,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公
共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

Input

输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来
m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,
字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n

Output

 对于每一次询问,输出答案。

Sample Input

5 5
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4

Sample Output

1
1
2
2
2

这是一道后缀自动机的好题。。。。至少我觉得是,因为我很菜


但使用SA的人可以无视了。。


然后我最主要的东西是从这里学的:点这里


在这里我学会了很多东西:

1.有关于SAM parent树的理解与应用

2.有关线段树合并的小姿势


根据题解,我们可以先把字符串反过来,然后问题就转换为:给定两个子串a和b,求的a所有子串和b的最长公共后缀的最大值。


然后我们当然要先二分答案啦

那么如何判断呢?

我们可以现在parent树上进行倍增,就可以得到下图中(已将串翻转)ec这一段在SAM上的点,因为parent树上面的点都拥有共同的后缀嘛!

SAM的常用技巧!!!我才不会告诉你我之前理解不是十分透彻呢!

找到点后就好办啦,我们只需要看一下这个right集合中是否有在a,b这一段区间的就可以了啊

至于如何弄出right集合请看下文

4556: [Tjoi2016&Heoi2016]字符串 后缀自动机 详细_第1张图片

接着自然就想到要知道一个点的right集合,注意,不是大小。因为一个点的right集合是他parent树上所有点的集合的并集(有点绕,请自己理解),所以这里可以用线段树合并可做,当然这里的线段树合并有一点点区别,因为一棵树要和多棵合并,然而之前的我做过的都只是上一棵(当然也有可能是我以前的模板太菜了)。


遇到的问题是见下图

4556: [Tjoi2016&Heoi2016]字符串 后缀自动机 详细_第2张图片

(画的很丑,我也不知道是谁画的,所以不要怪我)      但要是你们在别的博客上看到这幅图,那他肯定是抄的


大意就是y是x和z在parent树上的父亲,然后他的right集合就是x和z合并起来,然而如果你直接搞,会发现到最后x多了一个右儿子,这肯定错了,因为当你访问到x时,你会发现他多了一个儿子的儿子=孙子


然后这怎么办呢?我想了很久,于是膜了一下代码,于是就涨姿势了。大概就是要新建点啊(这个想到了,于是我码了一个十分垃圾的东西,这个就不说了),每次对于y都新建一个,具体看代码

还有一点小细节是这题的线段树不需要维护任何东西,只需要知道有没有建立过某一段区间就好了,因为你只需要知道有没有嘛


然后这题大概就做完了吧。。还有什么不懂的可以看看代码(我的代码比较丑,求right的时候还是递归的,接着变量名可能不是常见的i,j,(⊙o⊙)…代码风格因人而异啦),当然也可以留言


时间复杂度O(n*log^2n)

至于空间的话,大概就是线段树的空间吧,略微有点大

似乎没有SA+主席树的优越,那我为什么要用SAM呢,因为我要加深理解啊!


#include
#include
#include
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>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;
}



你可能感兴趣的:(字符串)