后缀自动机算法复习回顾

讲解

本篇博文其实只是笔者个人的做法复习笔记罢了,

如果是为学习后缀自动机不小心点进来的看官们,

我推荐先看 史 上 最 通 俗 的 后 缀 自 动 机 详 解 史上最通俗的后缀自动机详解 ,名副其实。

实现

0.Structure

struct SAM{
    
    
	int s[MAXC],fa;
	int len;
	SAM(){
    
    memset(s,0,sizeof(s));fa=len=0;}
}sam[MAXN<<1];

1.建立:插入点

新点 c ( c h a r ) c_{(char)} c(char) 插入末端 → l e n n p = + + c n t len_{np=++cnt} lennp=++cnt 赋为 l e n p = l a s t + 1 len_{p=last}+1 lenp=last+1
→ 往 p p p 的父亲找, p = f a p p=fa_p p=fap,沿途把无 s [ c ] s[c] s[c] 的点连向 n p np np
然后 →
① ① 直到结束无 s [ c ] s[c] s[c] f a n p fa_{np} fanp 赋为 1。

② ② 找到第一个 s [ c ] s[c] s[c] 记为 q q q
                         C a s e    # 1    \;\;\;\;\;\;\;\;\;\;\;\;Case\;\#1\; Case#1 l e n q = = l e n p + 1 len_q==len_p+1 lenq==lenp+1,直接把 f a n p fa_{np} fanp 赋为 q q q

                         C a s e    # 2    \;\;\;\;\;\;\;\;\;\;\;\;Case\;\#2\; Case#2 l e n q > l e n p + 1 len_q > len_p+1 lenq>lenp+1 e n d p o s n p endpos_{np} endposnp(位置集) 不是 e n d p o s q endpos_q endposq 的子集,不能连父亲,于是把 q q q 拆了:
新建点 n q = + + c n t , s a m [ n q ] = s a m [ q ] nq=++cnt,sam[nq]=sam[q] nq=++cnt,sam[nq]=sam[q],把 q q q 先全部复制到 n q nq nq
l e n n q = l e n p + 1 len_{nq}=len_p+1 lennq=lenp+1
→ 把 f a q fa_{q} faq f a n p fa_{np} fanp 赋为 n q nq nq
→ 继续往 p p p 的父亲找, p = f a p p=fa_p p=fap ,把 s [ c ] = = q s[c]==q s[c]==q 的改为连向 n q nq nq

图示:

在这里插入图片描述

模板:

int las = 1,cnt = 1;
int f[MAXN<<1]; //extra
void addsam(int c) {
    
    
	int p = las; int np = (las = ++ cnt); 
	f[np] = 1; //extra
	sam[np].len = sam[p].len + 1;
	for(;p && !sam[p].s[c];p = sam[p].fa) sam[p].s[c] = np;
	if(!p) sam[np].fa = 1;
	else {
    
    
		int q = sam[p].s[c];
		if(sam[q].len == sam[p].len + 1) sam[np].fa = q;
		else {
    
    
			int nq = ++ cnt;sam[nq] = sam[q];
			sam[nq].len = sam[p].len + 1;
			sam[np].fa = sam[q].fa = nq;
			for(;p && sam[p].s[c] == q;p = sam[p].fa) sam[p].s[c] = nq;
		}
	}
	return ;
}

2.处理出现次数

注意到上面注释 //extra 的地方,即是出现次数的初步处理
然后根据 f a fa fa p a r e n t    t r e e parent\;tree parenttree 建出来,从叶子开始加上去

模板:

vector<int> g[MAXN<<1];
void build_Parent_Tree() {
    
    
	for(int i = 2;i <= cnt;i ++) g[sam[i].fa].push_back(i);
}
void dfs(int x) {
    
    
	for(int i = 0;i < (int)g[x].size();i ++) {
    
    
		dfs(g[x][i]);
		f[x] += f[g[x][i]];
	}
}

3.用法

  • SAM 和 Parent Tree 高度统一,SAM 上每个点存的出现次数同时也是 Parent Tree 上每一等价类中所有子串出现次数
  • Parent Tree 中的父边在字符串匹配时可以当 AC 自动机中的 fail 边用
  • 由于 SAM 是个 DAG ,可以进行 DAG 的各种操作,拓扑、DP、…
  • ( T o    b e    c o n t i n u e d ) (To\;be\;continued) (Tobecontinued)

猜你喜欢

转载自blog.csdn.net/weixin_43960414/article/details/111990471