广义SAM 多字符串最长公共子串 SPOJ Longest Common Substring II

该博客探讨了一种利用Generalized Suffix Tree (GSAM)算法求解最长公共子序列(LCS)的问题。首先,对每个字符串构建Trie树,然后通过构建SAM(Suffix Array Machine)来优化Trie树。在SAM构造过程中,通过更新节点的后缀信息并进行基数排序,可以有效地计算出所有字符串的最长公共子序列的长度。最终,通过对SAM的遍历,可以找到最大长度的公共子序列。
摘要由CSDN通过智能技术生成

题目链接SPOJ.com - Problem LCS2

对每个串建好trie树,然后对trie建SAM

考虑i节点在parent树上两个子节点j和k,那么对于j和k来说,标记在j或k上代表的就是这个节点的所有后缀出现在某个字符串中,故要向上更新其所有后缀,表示对于其所有后缀来说,这些后缀都出现在了某个字符串中。
所有向上跳link的时候不是取与,而是取并。

#include<bits/stdc++.h>
using namespace std;
#define se second
#define fi first
#define LINF 0x3f3f3f3f3f3f3f3f
#define INF 0x3f3f3f3f
#define ll long long
#define pii pair<ll,ll>
const int mod=998244353;
const int N=2000005;
#define INF 0x3f3f3f3f
int tree[N][27];
int fa[N];
char ch[N];
int id1=1;
int have[N][11];
void insert(string s,int op){
	int u=1;
	for(auto x:s){
		if(!tree[u][x-'a'])tree[u][x-'a']=++id1,fa[id1]=u,ch[id1]=x-'a';
		u=tree[u][x-'a'];have[u][op]++; 
	}
}
struct GSAM{
	int nex[N][27],link[N],len[N];
	int cnt[N][12];
	int siz[N],pos[N];//pos为trie树上对应节点所对应在SAM上的节点编号
	int idx=1;//在GSAM中SAM为tree上的fa节点
	int sam_extend(int c,int last){
		int cur=++idx;int p=last;
		len[cur]=len[last]+1;
		while(p&&!nex[p][c]){
			nex[p][c]=cur;
			p=link[p];
		}
		if(!p){
			link[cur]=1;
		}
		else{
			int q=nex[p][c];
			if(len[p]+1==len[q])link[cur]=q;
			else{
				int clone=++idx;
				len[clone]=len[p]+1;
				memcpy(nex[clone],nex[q],sizeof nex[q]);
				link[clone]=link[q];
				while(p&&nex[p][c]==q){
					nex[p][c]=clone;
					p=link[p];
				}
				link[cur]=link[q]=clone;
			}
		}
		return cur;
	}
	void build(){
		queue<int>que;
		for(int i=0;i<26;i++){
			if(tree[1][i])que.push(tree[1][i]);
		}
		pos[1]=1;
		while(!que.empty()){
			int u=que.front();que.pop();
			pos[u]=sam_extend(ch[u],pos[fa[u]]);//以trie树上的父亲节点为last
			memcpy(cnt[pos[u]],have[u],sizeof have[u]);
			for(int i=0;i<26;i++){
				if(tree[u][i])que.push(tree[u][i]);
			}
		}
	}
	ll getans(){//求本质不同串
		ll ans=0;
		for(int i=2;i<=idx;i++){
			ans=ans+len[i]-len[link[i]];
		}
		return ans;
	}
	void rdix_sort(){//基数排序求出现次数,从最长节点开始向上link传值
		int bask[N];int q[N];//q[i]表示排第i个的是第几个节点
		for(int i=1;i<=idx;i++)bask[len[i]]++;//最长就idx
		for(int i=1;i<=idx;i++)bask[i]+=bask[i-1];
		for(int i=1;i<=idx;i++)q[bask[len[i]]--]=i;
		for(int i=idx;i>=1;i--){
			siz[link[q[i]]]+=siz[q[i]];
			for(int j=0;j<=10;j++){
				cnt[link[q[i]]][j]+=cnt[q[i]][j];
			}
		}
	}
}M1;
void solve(){
	int n;string s1;
	while(cin>>s1){
		n++;
		insert(s1,n);
	}
	M1.build();
	M1.rdix_sort();
	int maxn=0;
	for(int i=1;i<=M1.idx;i++){
		bool flag=false;
		for(int j=1;j<=n;j++){
			if(!M1.cnt[i][j])flag=true;
		}
		if(!flag){
			maxn=max(M1.len[i],maxn);
		}
	}
	cout<<maxn;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
		solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值