洛谷 SP1811 LCS - Longest Common Substring 【后缀自动机】

传送门
之前就用后缀数组做过,用字符串哈希做过,现在又用后缀自动机做一次。
用后缀自动机求最长公共子串有两种做法,一是对 \(s1\) 建后缀自动机,然后把 \(s2\) 拿上去匹配。我用的是这个方法。
二是对 \(s1,s2\) 建广义后缀自动机,然后直接算出每个节点的 \(endpos_{1,2}\),求 \(endpos_1,endpos_2\) 都为真的节点的 \(len\)。这个方法在之后的题中有体现。
说一下在后缀自动机上匹配的操作的一些细节,都是在做的过程中想通的一点东西。
\(length\) 为已匹配长度,从节点 \(p=1\) 开始匹配,如果 \(ch[p][c]\) 为真,那么 \(p=ch[p][c],length++\),这个操作很好理解,就是成功匹配。
如果 \(ch[p][c]\) 为假,那么就将 \(p\)\(fa[p]\) 跳,实际上就是在已匹配的部分上扣掉前缀,直到 \(ch[p][c]\) 为真为止,然后重置 \(length\)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
typedef long long LL;
const int N=3e5+10;
char s[N],t[N];
int n,m,ans;
struct SuffixAutoMachine{
	int last=1,tot=1,ch[N*2][26],fa[N*2],len[N*2];
	int newnode(int id){fa[++tot]=fa[id];len[tot]=len[id];memcpy(ch[tot],ch[id],sizeof(ch[tot]));return tot;}
	void insert(int c){
		int p=last,np=last=newnode(0);
		len[np]=len[p]+1;
		for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
		if(!p) {fa[np]=1;return;}
		int q=ch[p][c];
		if(len[q]==len[p]+1) {fa[np]=q;return;}
		int nq=newnode(q);len[nq]=len[p]+1;
		fa[q]=fa[np]=nq;
		for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 
	}
	void init(){
		for(int i=1;i<=n;i++) insert(s[i]-'a');
		for(int i=1,leng=0,p=1;i<=m;i++){
			if(ch[p][t[i]-'a']) leng++,p=ch[p][t[i]-'a'];
			else {
				for(;p&&!ch[p][t[i]-'a'];p=fa[p]);
				if(p) leng=len[p]+1,p=ch[p][t[i]-'a'];
				else p=1,leng=0;
			}
			ans=max(ans,leng);
		}
	}
}sam;


int main(){
	scanf("%s%s",s+1,t+1);
	n=strlen(s+1);
	m=strlen(t+1);
	sam.init();
	printf("%lld\n",ans);
	return 0;
}
posted @ 2020-04-09 23:57  BakaCirno  阅读(115)  评论(0编辑  收藏  举报