扩展kmp

/*
扩展kmp
求对于原串S1的每一个后缀子串与模式串S2的最长公共前缀 
nxt[i]表示为S2中以i为起点的后缀字符串和S2的最长公共前缀长度
extend[i]表示为以S1中以i为起点的后缀字符串和S2的最长公共前缀长度
复杂度:O(n+m) 
*/
#include <iostream>
#include <string>
using namespace std;

int nxt[100005],extend[100005];

void get_next(string a)
{
	int i = 0,j,po;
	int len = a.length();
	nxt[0] = len; 
	while( a[i] == a[i+1] && i + 1 < len )
	{
		i ++;
	}
	nxt[1] = i;
	po = 1;
	for (i = 2; i < len; i++)
	{
		if( nxt[i-po] + i < nxt[po] + po )
		{
			nxt[i] = nxt[i-po];
		}else
		{
			j = nxt[po] + po - i;
			if( j < 0 ) j = 0;
			while( i + j < len && a[j] == a[j+i] )
			{
				j ++;
			}
			nxt[i] = j;
			po = i;
		}
	}
}

void exkmp(string a,string b)
{
	int i = 0,j,po;   //extend[po] + po = 经匹配的最远位置; 
	int len1 = a.length();
	int len2 = b.length();
	get_next(b);
	while( a[i] == b[i] && i < len1 && i < len2 ) //计算extend[0]
	{
		i++;
	}
	extend[0] = i;
	po = 0;//初始化po的位置
	for(i = 1; i < len1; i++) 
	{
		/*记p+1为匹配的最远位置
		po+extend[po]-1=P,可以得知S1[po,P]=S2[0,P-po],
		所以S1[i,P]=S2[i-Po,P-Po],令len=next[i-Po]
		*/ 
		if(i + nxt[i-po] < extend[po] + po)
		/*
		S2[0,len-1] = S2[i-Po,i-Po+len-1],
		所以S1[i,i+len-1]=S2[i-Po,i-Po+len-1]=S2[0,len-1],即extend[i]=len;
		*/ 
			extend[i] = nxt[i-po];
		else 
		{ /*第二种情况,要继续匹配才能得到extend[i]的值
			S1[Po,P]=S2[0,P-Po]
			可得出S1[i,P]=S2[i-Po,P-po],len=next[i-Po],所以S2[0,len-1]=S2[i-Po,i-Po+len-1]
			所以S1[i,p]=S2[0,P-i-1]
			由于大于P的位置我们还未进行匹配,所以从原串S1的P位置开始和模式串的P-i位置开始进行逐一匹配,直到匹配失败。
			并更新相应的Po位置和最远匹配位置P,此时extend[i]=P-i
			*/
			j = extend[po] + po - i;
			if( j < 0 ) j = 0;//如果i>ex[po]+po则要从头开始匹配
			while( i + j < len1 && j < len2 && a[i+j] == b[j] )//计算ex[i]
			{
				j++;	
			}
			extend[i] = j;
			po = i;//更新po的位置
		}
	}
}

int main()
{
	string a,b;
	cin >> a >> b;
	exkmp(a,b);
	int len1 = a.length();
	int len2 = b.length();
	for (int i = 0; i < len2; i++)
	{
		if( i != len2 - 1 ) printf("%d ",nxt[i]);
		else printf("%d\n",nxt[i]);
	}
	for (int i = 0 ; i < len1; i++)
	{
		if( i != len1 - 1 ) printf("%d ",extend[i]);
		else printf("%d\n",extend[i]);
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值