/*
扩展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;
}
扩展kmp
最新推荐文章于 2023-08-17 12:02:50 发布