sam备忘

学习链接

鉴于sam写一次忘一次,在这里记录一下自己对sam的一些小小定义和理解
l e n [ i ] : i 节 点 所 代 表 的 字 符 串 中 最 长 的 一 个 的 长 度 len[i]:i节点所代表的字符串中最长的一个的长度 len[i]:i
f a [ i ] : i 节 点 在 p a r e n t 树 上 的 父 亲 , i 这 个 状 态 的 后 缀 链 接 fa[i]:i节点在parent树上的父亲,i这个状态的后缀链接 fa[i]:iparent,i
e d : 当 前 可 以 接 收 节 点 的 节 点 编 号 ( 整 个 字 符 串 在 S A M 上 的 状 态 节 点 ) ed:当前可以接收节点的节点编号(整个字符串在SAM上的状态节点) ed:SAM
c h [ i ] [ c ] : i 节 点 加 一 个 字 符 c 后 可 以 转 移 到 哪 里 ch[i][c]:i节点加一个字符c后可以转移到哪里 ch[i][c]:ic
p o s [ i ] : 第 i 个 节 点 的 某 个 e n d p o s , 注 意 在 做 第 二 种 类 型 的 转 移 的 时 候 不 要 忘 了 更 新 这 个 pos[i]:第i个节点的某个endpos,注意在做第二种类型的转移的时候不要忘了更新这个 pos[i]:iendpos,

int fa[maxn],len[maxn],ed=1,ct=1;//这里很重要,构建sam的时候0这个位置代表虚拟状态,需要空出来
map<int,int> ch[maxn];
int pos[maxn]
inline void insert(int c,int i){//这里是在线构建sam的函数
	int np=++ct,p=ed;ed=ct;len[np]=len[p]+1;pos[np]=i;
	for(;!ch[p][c];p=fa[p])ch[p][c]=np;
	if(!p)fa[np]=1;
	else{//进入这里说明我们要添加一个已经存在的字符串x+c,其中x是原串的一个后缀,所以我们不能增加再新的转移,而是考虑将新节点接在哪,发现需要接的位置q应该满足len[q]==len[p]+1
		int q=ch[p][c];
		if(len[q]==len[p]+1)fa[np]=q;//如果满足条件就直接做
		else{//但是如果不满足条件,意味着q还对应一些更长的字符串,所以我们新建一个nq把我们需要的转移提出来,再处理一下节点的关系就好了
			int nq=++ct;len[nq]=len[p]+1;ch[nq]=ch[q];
			pos[nq]=pos[q];//这句话很重要,很容易忘记,记住每一个新节点都需要赋值endpos
			fa[nq]=fa[q];fa[q]=fa[np]=nq;
			for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
		}
	}
}

高阶技巧:
在建出parent树之后,我们在parent树上dfs时,可能要按照字典序顺序进行dfs (例:2021CCPC东北省赛L题)
此时由于parent树的边上没有转移的字母信息,要怎么得到从 f a [ u ] fa[u] fa[u]转移来的字母 c c c呢?

c = = s [ p o s [ u ] − l e n [ f a [ u ] ] ] c == s[pos[u]-len[fa[u]]] c==s[pos[u]len[fa[u]]]
因为我们知道fa[u]节点所代表的字符串是u所代表的节点的字符串的一些后缀,
那么减去其中最长的一个长度,我们就得到了属于从fa[u]转移到u的节点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值