主要思想:当主串第i个字符与模式串第j个字符匹配
不等是不需要回朔i指针,而是利用已经得到的
部分匹配结果next数组,将模式串向右滑动
核心:当主串中第i个字符与模式串第j个字符匹配失败时,
主串中第i个字符(i不回朔)应与模式串中哪个字符匹配??
这就要构建一个next数组,来存放当匹配失败应该在匹配的位置编号
怎么定义next函数呢?
若令next[j]=k,当匹配失败时主串中第i个字符与模式串中第j个字符
比较不等时,仅需要将模式串向右滑动k个字符再和主串第i个字符比较就行
此时,模式串中k-1个字符t1t2t3t4tk-1必定与主串中第i个字符之前长度为k-1
的子串相等
0 j=1(t1与sj比较不等时,下一步进行t1与si+1的比较)
next[j] = max {k|1<k<j 且有 "t1t2t3tk-1" = "sj-k+1sj-k+2...si-1"}
0 k = 1(不存在向东子串,下一步进行t1与s1的比较)
意思就是在模式串中是否存在一个序列与从头带此的序列相等
相等则next值为从头带此的长度
j 1 2 3 4 5 6 7 8
模式串 a b a a b c a c
next[j] 0 1 1 2 2 3 1 2
在模式串中,next[1] = 0,
j = 2 b前面不存在一个子序列与从头开始的子序列相等 所以 next[2] = 1
j = 3 a前面不存在一个子序列与从头开始的子序列相等 所以 next[3] = 1
j = 4 a前面存在一个a长度为1的子序列与重头开始长度为1的子序列相等,所以 next[4] = 2
j = 5 b前面存在一个长度为1的子序列a与从头时开始长度1的子序列相等,所以 next[5] = 2
j = 6 c前面存在一个长度为2的子序列ab与重头开始长度为2的序列相等, 所以 next[6] = 3
j = 7 a前面不存在一个子序列与从头开始的子序列相等 所以 next[7] = 1
j = 8 c前面存一个长度为1的子序列a与从头开始的长度1的子序列相等 所以 next[8] = 2
#include <iostream>
#include <string>
using namespace std;
int next[100];
void getnext(char str[])
{
int i = 1,j = 0;
next[1] = 0;
int len = strlen(str);
while(i <= len)
{
if(j == 0 || str[i] == str[j])
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
for(i = 1; i < len; i++)
{
cout<<next[i];
}
cout<<endl;
}
int KMP(char S[],char str[])
{
int i = 1,j = 1;
int lenS = strlen(S),lentr = strlen(str);
while(i <= lenS && j <= lentr)
{
if(S[i] == str[j] || j == 0)
{
i++;
j++;
}
else
{
j = next[j];
}
}
if(j == lentr+1)
return i - lentr + 1;
else
return -1;
}
int main()
{
char str[20],S[100];
cin>>S+1;
cin>>str+1;
getnext(str);
int pos = KMP(S,str);
if(pos == -1)
cout<<"不存在"<<endl;
else
cout<<"位置:"<<pos<<endl;
return 0;
}