KMP & AC自动机

KMP

AC自动机

用于解决多模式串匹配的状态机,本质上为\(trie+KMP\)

\(trie\)上匹配,失配时,利用\(KMP\)的思想,尝试跳到能匹配的最长后缀,这样可以尽可能使其产生贡献

使用失配数组\(fail[]\)记录该最长后缀的对应节点

失配指针

性质1:\(x\)最长后缀为\(fail[x]\),次长后缀为\(fail[fail[x]]\),第三长后缀为\(fail[fail[fail[x]]]\)……(易证)

性质2:所有\(fail\)指针形成一棵树(\(fail\)树)

由性质1,对于一个点\(x\),我们可以通过不断跳\(fa[x]\)\(fail\)指针,找到第一个可以拓展到\(x\)的后缀的点,赋值\(fail[x]\)儿子即可

由于匹配时,\(x\)匹配成功,所有的\(fail^k[x]\)都可以成功匹配,一个个跳\(fail\)会很慢,可以利用性质2,做个树上操作来加速(差分,树剖等)

由于\(x\)的后缀一定比\(x\)短,根据\(trie\)上的深度\(BFS\)即可保证正确性

\(p.s.\) 其实最快的跳\(fail\)是预处理\(mov[MAXN][26]\)

细节

根结点(\(0\)号点)的儿子特殊处理,因为跳\(fail\)的边界是跳到\(0\),直接做会导致自己指向自己

代码

void Getfail() {
	queue<int> q;
	for(int i=0;i<26;i++) {	//特殊处理
		if(son[rt][i]) {
			q.push(son[rt][i]);
		}
	}
	while(!q.empty()) {
		int x=q.front(); q.pop();
		for(int i=0;i<26;i++) {
			if(son[x][i]) {
				q.push(son[x][i]);
				int tp=fail[x];		//核心代码块
				while(tp&&!son[tp][i]) tp=fail[tp];
				if(son[tp][i]) fail[son[x][i]]=son[tp][i];
				else fail[son[x][i]]=tp;
			}
		}
	}
}
posted @ 2024-11-11 10:54  Zhone_lb  阅读(15)  评论(0)    收藏  举报