BZOJ 2946 Poi2000 公共串 后缀自动机

题目大意:求n个串的最长公共子串

太久没写SAM了真是……

将第一个串建成后缀自动机,用其它的串进去匹配

每个节点记录每个串在上面匹配的最大长度

那么这个节点对答案的贡献就是所有最大长度的最小值

对所有贡献取最大就行了= = 这最大最小看着真是别扭

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 10100
using namespace std;
int n,ans;
char s[M];
namespace Suffix_Automaton{
	struct SAM{
		SAM *parent,*son[26];
		int max_len,min_len[6];
		bool v;
		SAM(int _=0):parent(0x0),max_len(_),v(false)
		{
			memset(son,0,sizeof son);
			memset(min_len,0,sizeof min_len);
		}
	}*root=new SAM,*last=root;
	void Extend(int x)
	{
		SAM *p=last;
		SAM *np=new SAM(p->max_len+1);
		while(p&&!p->son[x])
			p->son[x]=np,p=p->parent;
		if(!p) np->parent=root;
		else
		{
			SAM *q=p->son[x];
			if(p->max_len+1==q->max_len)
				np->parent=q;
			else
			{
				SAM *nq=new SAM(p->max_len+1);
				nq->parent=q->parent;
				memcpy(nq->son,q->son,sizeof nq->son);
				q->parent=nq;np->parent=nq;
				for(;p&&p->son[x]==q;p=p->parent)
					p->son[x]=nq;
				nq->min_len[1]=nq->max_len;
			}
		}
		last=np;
		np->min_len[1]=np->max_len;
	}
	void DFS(SAM *p)
	{
		int i,temp=0x3f3f3f3f;
		for(i=1;i<=n;i++)
			temp=min(temp,p->min_len[i]);
		ans=max(ans,temp);
		p->v=true;
		for(i=0;i<26;i++)
			if( p->son[i] && !p->son[i]->v )
				DFS(p->son[i]);
	}
}
int main()
{
	using namespace Suffix_Automaton;
	int i,j;
	cin>>n;
	scanf("%s",s+1);
	for(i=1;s[i];i++)
		Extend(s[i]-'a');
	for(i=2;i<=n;i++)
	{
		scanf("%s",s+1);
		SAM *p=root;int len=0;
		for(j=1;s[j];j++)
		{
			while( p!=root && !p->son[s[j]-'a'] )
				p=p->parent,len=p->max_len;
			if( p->son[s[j]-'a'] )
				p=p->son[s[j]-'a'],len++;
			for(SAM* temp=p;temp;temp=temp->parent)
				temp->min_len[i]=max(temp->min_len[i],min(len,temp->max_len) );
		}
	}
	DFS(root);
	cout<<ans<<endl;
}
时间: 2024-10-13 23:35:56

BZOJ 2946 Poi2000 公共串 后缀自动机的相关文章

BZOJ 2946 POI2000 公共串 后缀自动机(多串最长公共子串)

题意概述:给出N个字符串,每个串的长度<=2000(雾...可能是当年的年代太久远机子太差了),问这N个字符串的最长公共子串长度为多少.(N<=5) 抛开数据结构,先想想朴素做法. 设计一种稳定的暴力算法.可以想到这样一种做法:首先确定一个串,枚举每个位置,然后暴力计算其他每个串以这个位置开头的最长匹配,取最小值,就是在公共子串在我们确定下来的串的这个位置开头的时候所能得到的最长公共子串.不难发现把这个问题转化成后缀的形式也是一样的.同时发现可能在枚举多个位置的时候答案甚至最后构造出来的串都是

[BZOJ2946][Poi2000]公共串 后缀自动机

2946: [Poi2000]公共串 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1367  Solved: 612[Submit][Status][Discuss] Description 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单词 l        计算最长公共子串的长度 l        输出结果 Input 文件的第一行是整数 n,1<=n<=5,表示单词的数量.接下来n行每行一个单词

BZOJ 2946: [Poi2000]公共串

Description 最长公共子串,\(n\leqslant 5,l\leqslant 1000\) Solution SAM... 对于同一字符串取max,不用字符串取min Code /************************************************************** Problem: 2946 User: BeiYu Language: C++ Result: Accepted Time:1012 ms Memory:2308 kb ******

【BZOJ】2946: [Poi2000]公共串

http://www.lydsy.com/JudgeOnline/problem.php?id=2946 题意:给n个串,求最大公共子串.(1<=n<=5,每个串长度<=2000) #include <bits/stdc++.h> using namespace std; const int N=2005<<1; struct sam { int cnt, root, last, l[N], c[N][26], f[N], p[N], mx[N], mxl[N];

[POI2000]公共串 - 后缀数组

Description 求若干个串的最长的公共子串的长度. Solution 考虑将这若干个串全部拼起来,中间用一些不在字符集内的符号隔开. 然后二分答案 \(K\),如果连续的一段 \(height\) 都大于等于 \(K\),且每个串都出现了至少一次,则是可行的. Code #include <bits/stdc++.h> using namespace std; const int _ = 1e5 + 10; int N, n, s[_], belong[_]; int rnk[_],

[BZOJ2946] [Poi2000]公共串解题报告|后缀数组

给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 单词个数<=5,每个单词长度<=2000 尽管最近在学的是SAM...但是看到这个题还是忍不住想写SA... (其实是不知道应该怎么用SAM做... 对于后缀数组而言,多个字符串的公共子串与两个处理起来并没有什么区别 只要在中间加一些没有用的字符,将多个字符串拼成一个字符串 然后二分答案,对于一个长度L,在一组除了开头其他height都>=L的区间中如果每个字符串的位置都出现过就可以 应该是第二次这么解决一道公共串的题了.. 然

BZOJ 4032 HEOI2015 最短不公共子串 后缀自动机+序列自动机+BFS

题目大意:给定字符串A和B,求A最短的子串/子序列S满足S不是B的子串/子序列 这题真TM有毒*2 搞法类似这道题 然后子串是后缀自动机 子序列自然就是序列自动机了= = 每更新一个x节点时所有没有x的后继的节点都连向这个节点 每个节点的parent是这个字母上一次出现的位置 每个字母记录最后一次出现的位置 更新指针时沿着parent指针撸一遍就行了 #include <cstdio> #include <cstring> #include <iostream> #in

[Poi2000]公共串

1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 #include <cstring> 6 #define maxn 4005 7 #define maxm 2005 8 using namespace std; 9 10 int n,m,tot,last,root,len,smin[maxn],sum[maxn],tmp[m

[Poi2000]公共串 &amp;&amp; hustoj2797

传送门:http://begin.lydsy.com/JudgeOnline/problem.php?id=2797 题目大意:给你几个串求出几个串中的最长公共子串. 题解:先看n最大才5,所以很容易想到暴力写法,因为最近在学后缀自动机就写写后缀自动机吧. 我们将第一个串作为母串,然后在用其他的串与它进行匹配,并且记录下其匹配中每个状态的最大匹配数,答案则为每个状态的最大匹配的最小值中的最大值..(绕晕了) 1 #include<iostream> 2 #include<algorith