KMP算法使得主串(目标串)指针不必回溯而只需回溯子串(模式串)指针,并且子串指针也不一定需要回溯到子串的第一个字符。算法的时间复杂度是O(m+n)
【问题描述】KMP算法是字符串模式匹配算法中较为高效的算法之一,其在某次子串匹配母串失败时并未回溯母串的指针而是将子串的指针移动到相应的位置。
【输入形式】3组字符串,每组字符串占一行。每行包含由空格分隔的两个字符串,字符串仅由英文小写字母组成且长度不大于100。
【输出形式】每组数据输出1行,输出后一个字符串在前一个字符串中的位置,如果不匹配,则输出0。
【样例输入】
string str
thisisalongstring isa
nosubstring subt【样例输出】
1
5
0
【提示】表示字符串的数据结构可以是字符数组或用串类实现。KMP算法调用很简单,但难的是理解算法的思想。掌握算法的思想才能说是掌握算法。
next数组的求解以及输出:
#include <iostream>
#include <cstring>
using namespace std;
#define Maxsize 1000
char b[Maxsize];int next[Maxsize];
void Operator_next()
{
int lenb=strlen(b);
next[0]=-1; //循环内部会使用到这个-1
next[1]=0; //此处这条语句也是可以在循环内部进行赋值的
for(int i=2;i<lenb;i++)
{
int k=next[i-1];
if(b[k]==b[i-1]) next[i]=k+1;
else
{
while(k!=-1)
{
k=next[k];
if(k==-1)break;
if(b[k]==b[i-1])
{
next[i]=k+1;
break;
}
}
if(k==-1)next[i]=0;
}
}
}
void Output()
{
int lenb=strlen(b);
for(int i=0;i<lenb;i++)cout<<next[i]<<" ";
}
int main()
{
cin>>b;
Operator_next();
Output();
return 0;
}
思路:一般是将next[0]赋值为-1,之后便是看中间部分next数组的求解过程。
假设现在遍历到的位置是数组b的第i个位置,并设next[i-1]=k,则是需要看b[k]的值与b[i-1]的值是否相同了:
(一)如果b[k]与b[i-1]是相同的,则next[i]=next[i-1]+1;
例如:看下面的测试数据1,当i=5时,也就是现在要求的是next[5](也就是next数组前面的值已经求出来了);那么这个时候就要看next[i-1]的值,其中next[i-1]=1,则令k=next[i-1]=1,用k将这个时候i-1所对应的next的值记录下来;那么之后就要b[k]和b[i-1]是否相同了,如果相同则意味着next[i]=k+1=next[i-1]+1.
(二)如果b[k]与b[i-1]并不相同的时候,(我刚开始就是想既然都不一样了,那么子串进行回溯的时候肯定就是回溯到0就可以了,也就是将next[i]赋值为0。但是运行了下面的测试数据二之后,我就发现不对了,也就是这种情况下并不是像想象中的那么简单的,是需要回溯next数组进行一些判断的)
即:当b[k]!=b[i-1]的时候,就是需要回看next[k]的,并将k的数值进行更新,也就是k=next[k],然后就是接着看b[k]和b[i-1]是否相同;如果仍然不相同,则是接着进行回看。
结束回看的条件要么是b[i]=b[k]了,将next[i]赋值为k+1;要么就是会看到了next[0](也就是k=-1)仍然没有使得b[k]=b[i-1],那么就将next[i]的值赋为0就可以了。
例如:看下面的测试数据2
(1)当i=4的时候,k=next[i-1]=1,但是b[k]!=b[i-1](也就是b[1]=b不等于b[3]=a)所以就令k=next[k]=0,然后就再次比较b[k]和b[i-1]结果发现b[k]=b[0]=a是等于b[i-1]=b[3]=a的,所以就停止回看,对next[i]进行赋值,next[i]=k+1=1
(2)当i=6的时候,k=next[i-1]=2,但是b[k]!=b[i-1](b[k]=b[2]=a,而b[i-1]=b[5]=c,两者并不相等)所以就要开始进行回看;令k=next[k]=next[2]=0,然后就看b[k]和b[i-1]是否相同,及结果发现b[k]=b[0]=a,b[i-1]=b[5]=c,两者并不相同,然后就是接着回看;令k=next[k]=-1,发现k=-1,就停止回看,将next[i]的值赋为0
几组next数组的测试数据:
子串b | a | a | b | a | a | c |
next | -1 | 0 | 1 | 0 | 1 | 2 |
子串b | a | b | a | a | b | c | a | c |
next | -1 | 0 | 0 | 1 | 1 | 2 | 0 | 1 |
子串b | a | a | b | a | a | b | a | a | b | a | a | c |
next | -1 | 0 | 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
这道题的代码:
#include <iostream>
#include <cstring>
using namespace std;
#define Maxsize 1000
char a[Maxsize],b[Maxsize];int next[Maxsize];
void Operator_next()
{
int lenb=strlen(b);
next[0]=-1;
next[1]=0;
for(int i=2;i<lenb;i++)
{
int k=next[i-1];
if(b[k]==b[i-1]) next[i]=k+1;
else
{
while(k!=-1)
{
k=next[k];
if(k==-1)break;
if(b[k]==b[i-1])
{
next[i]=k+1;
break;
}
}
if(k==-1)next[i]=0;
}
}
}
int Match()
{
int lena=strlen(a),lenb=strlen(b),xb=0,i=0,j=0,len=0,Max=0;
while(i<lena&&j<lenb)
{
if(j==-1){i++;j=0;}
else if(a[i]==b[j]){i++;j++;len++;}
else
{
j=next[j];
if(Max<len){xb=i;Max=len;}
len=0;
}
}
if(Max<len){xb=i;Max=len;}
if(Max==lenb)return xb-Max+1;
return 0;
}
int main()
{
for(int i=0;i<3;i++)
{
cin>>a>>b;
Operator_next();
int xb=Match();
cout<<xb<<endl;
}
return 0;
}
注:以上仅仅是作业提交通过的代码,如有其他错误或者是可以进一步优化的地方,欢迎指出