SAM 学习笔记
WanXiangYun · · 个人记录
SAM 学习笔记
鲜花
那些生活在阴沟里的人却来指责阳光耀眼的人,还要试图用道德绑架你,而这种人却最没原则。
这个世界太浑浊了,有些人的恨没有原因的,因为他们平庸没有天赋,你的善良和才华都成了原罪。
我有好好做人的教养但也有不好惹的能力我的棍棒只会用来打那些得寸进尺的人。
——董宇辉
但是这并不属于我的世界啊。。。
生活有一种英雄主义,那就是认清了生活的真相,依然热爱生活。
——罗曼罗兰
SAM
设
性质:
推论:
进一步的:
$\text{Lemma 3.}$ 若有 $R(T_1)=R(T_k)$,并且 $|T_1|\leq |T_k|$,且 $T_1$ 是 $T_2$ 的后缀,$T_2$ 是 $|T_3|$ 的后缀 $……$ 依次类推。则 $R(T_1)=R(T_2)=\dots=R(T_k)$。
也就是说每一个节点对应的是一段长度在一定区间内的子串,并且依次存在后缀关系。
就是说
有限状态自动机
形如
设
-
-
$……
然后如果直接这样建出有向图,点数和边数都是爆炸的。。。
考虑
应用:求字符串
-
前置: 可以
\mathcal{O}(n) 得到字符串S[1\dots p] 在\text{DFA} 上对应的节点。 -
根据后缀树的性质,直接找到
T_1,T_2 在树上的\text{LCA} ,然后答案就是\text{val(LCA}(x,y)) 。
至于怎么建 SAM,直接背板子即可。。。
广义 SAM
简单来说就是按照
例题
luoguP6640 [BJOI2020] 封印
给出只包含小写字母
对于全部数据,满足
Idea
对于每一个
然后令
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 200010
int n,m,Q,len[N],lim[N];
char S[N],T[N];
int read ()
{
int k=1,s=0;char ch=getchar ();
while (!isdigit (ch)) {if (ch=='-') k=-1;ch=getchar ();}
while (isdigit (ch)) {s=s*10+ch-'0';ch=getchar ();}
return k*s;
}
struct NodeA
{
int idx,from;
struct NodeB
{
int son[2],fail,len;
}Tree[N<<2];
void Init ()
{
memset (Tree,0,sizeof (Tree));
idx=1,from=1;
}
int Extand (int len)
{
Tree[++idx].len=len;
return idx;
}
void Insert (int x)
{
int pre=from,now=Extand (Tree[pre].len+1);
while (pre && !Tree[pre].son[x])
{
Tree[pre].son[x]=now;
pre=Tree[pre].fail;
}
if (!pre) Tree[now].fail=1;
else
{
int _pre=Tree[pre].son[x];
if (Tree[_pre].len==Tree[pre].len+1)
Tree[now].fail=_pre;
else
{
int _now=Extand (Tree[pre].len+1);
Tree[_now].fail=Tree[_pre].fail;
for (int i=0;i<2;i++)
Tree[_now].son[i]=Tree[_pre].son[i];
while (pre && Tree[pre].son[x]==_pre)
{
Tree[pre].son[x]=_now;
pre=Tree[pre].fail;
}
Tree[_pre].fail=Tree[now].fail=_now;
}
}
from=now;
}
}SAM;
struct NodeC
{
int n,lg[N],maxv[21][N];
void Init (int m,int *a)
{
n=m;
lg[0]=-1;
for (int i=1;i<=n;i++)
{
lg[i]=lg[i>>1]+1;
maxv[0][i]=a[i];
}
for (int k=1;k<=20;k++)
for (int i=1;i+(1<<k)-1<=n;i++)
maxv[k][i]=max (maxv[k-1][i],maxv[k-1][i+(1<<(k-1))]);
}
int Query (int l,int r)
{
int k=lg[r-l+1];
return max (maxv[k][l],maxv[k][r-(1<<k)+1]);
}
}ST;
void Init ()
{
scanf ("%s %s",S+1,T+1);
n=strlen (S+1),m=strlen (T+1);
}
void Work ()
{
SAM.Init ();
for (int i=1;i<=m;i++)
SAM.Insert (T[i]-'a');
int pre=1,_len=0;
for (int i=1;i<=n;i++)
{
while (pre!=1 && !SAM.Tree[pre].son[S[i]-'a'])
{
pre=SAM.Tree[pre].fail;
_len=SAM.Tree[pre].len;
}
if (SAM.Tree[pre].son[S[i]-'a'])
{
pre=SAM.Tree[pre].son[S[i]-'a'];
_len++;
}
len[i]=_len,lim[i]=i-_len+1;
}
ST.Init (n,len);
Q=read ();
while (Q--)
{
int l=read (),r=read ();
if (lim[l]>=l)
printf ("%d\n",ST.Query (l,r));
else if (lim[r]<=l)
printf ("%d\n",r-l+1);
else
{
int p=lower_bound (lim+l,lim+r+1,l)-lim;
printf ("%d\n",max (p-l,ST.Query (p,r)));
}
}
}
signed main ()
{
Init ();
Work ();
return 0;
}
luoguP6793 [SNOI2020] 字符串
有两个长度为
对于所有数据,