【spoj2774】最长公共子串

题目描述:

给你两个字符串,求它们最长公共子串的长度,如果不存在公共子串则输出0。

样例输入:

yeshowmuchiloveyoumydearmotherreallyicannotbelieveit
yeaphowmuchiloveyoumydearmother

样例输出:

27


题解:

SAM果题。

代码+SAM理解记录附下:

//
//  Title : SAMachine
//  Date : 10.03.2016
//  Test : CODEVS-3160
//  Complexity : O(n)
//  
/*
	对于子串匹配/处理等问题——
	解决办法:后缀自动机
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

#ifdef WIN32
	#define LL "%I64d"
#else
	#define LL "%lld"
#endif

#ifdef CT
	#define debug(...) printf(__VA_ARGS__)
#else
	#define debug(...)
#endif

#define R register
#define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?EOF:*S++)
#define gmax(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define gmin(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
#define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0)
char B[1<<15],*S=B,*T=B;
inline int FastIn()
{
	R char ch;R int cnt=0;R bool minus=0;
	while (ch=getc(),(ch < '0' || ch > '9') && ch != '-') ;
	ch == '-' ?minus=1:cnt=ch-'0';
	while (ch=getc(),ch >= '0' && ch <= '9') cnt = cnt * 10 + ch - '0';
	return minus?-cnt:cnt;
}
#define maxn 250010
char str[maxn] ;
int ans ;
struct SAM
{
	int p , np , q , nq;
	int cnt , last;
	int a[maxn][26] , l[maxn] , fa[maxn];
	SAM()
	{
		last=++cnt;
	}
	void extend(R int c)
	{
		p = last ;	// p表示添加前的最后一个状态
		np = last = ++cnt;	// np表示当前的状态
		l[np] = l[p] + 1;	// 当前的长度为上一个状态的长度+1
		for (; !a[p][c] && p ; ) a[p][c] = np , p = fa[p] ;	// 如果p的fail指针的trie边没有被占用,那么就将其trie边连到np上
		if (!p) fa[np] = 1 ;	// 如果当前这单个字符没有出现过,那么就把当前状态的fail指针设为1(init,初始状态)
		else	// 如果p的fail指针被占用了,那么就————
		{
			q = a[p][c];	// q表示被占用掉的那个状态
			if (l[p] + 1 == l[q] ) fa[np] = q;	// 如果这个状态刚好是当前状态的上一个字符的状态,那么就意味着出现了两个相同的字符,此时就直接将fail指针建到q上
			else	// 否则就
			{
				nq = ++cnt ;	// 新建一个节点
				l[nq] = l[p] + 1 ;	// 新建的这个节点将会的长度等于当前状态的长度
				memcpy (a[nq] , a[q] , sizeof (a[q]) );	// 将当前占用掉的那个状态的trie边连到新的节点上
				fa[nq] = fa[q] ;	// 新建节点的fail指针指向上一个状态
				fa[np] = fa[q] = nq ;	// 当前的节点和被占用的节点的fail指针指向新的节点
				while (a[p][c] == q) a[p][c] = nq , p = fa[p] ;	// 把所有trie边指向q的点的trie边连向nq
			}
		}
	}
	void build()
	{
		scanf( "%s\n" , str );
		for (R int i = 0 ; str[i] ; i++ ) extend ( str[i] - 'a' ) ;
	}
	void solve()
	{
		scanf( "%s\n" , str );
		R int tmp = 0;
		for (R int i = 0 ; str[i] ; i++ )
		{
			R int c = str[i] - 'a';
			if ( a[p][c] ) p = a[p][c] , tmp ++ ;
			else
			{
				for ( ; p && !a[p][c] ; ) p = fa[p] ;
				if (!p) p = 1 ,tmp = 0;
				else tmp = l[p] + 1 , p = a[p][c];
			}
			cmax( ans , tmp );
		}
		printf("%d\n",ans );
	}
}sam;
int main()
{
	sam.build();
	sam.solve();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值