算法介绍
KMP算法是一种采用最长前后缀长度进行转移的字符串匹配模式
主要的操作对象是模式串(P)即子串
算法思想
KMP采用牺牲可控范围内的空间来节约时间上的成本
与暴力算法不同的是KMP算法用模式串P进行回溯
同时KMP算法采用了一种类似于倍增思想的方式处理模式串P的移动
元素
next[]存储了该位置字符串不匹配时模式串P的回溯位置
也存储了该位置之前的字符串的最长前后缀和
采用结构体来存储模式串P(子串)和文本串S(母串)
i,j作指针之用
代码实现
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define MaxSize 100010
using namespace std;
//LuckZY_
typedef struct
{
char data[MaxSize];
int length; //串长
}SqString;
//SqString 是串的数据结构
//typedef重命名结构体变量,可以用SqString t定义一个结构体。
void GetNext(SqString t,int next[]) //由模式串t求出next值
{
int j,k;
j=0;k=-1;
next[0]=-1;//第一个字符前无字符串,给值-1
while (j<t.length-1)
//因为next数组中j最大为t.length-1,而每一步next数组赋值都是在j++之后
//所以最后一次经过while循环时j为t.length-2
{
if (k==-1 || t.data[j]==t.data[k]) //k为-1或比较的字符相等时
{
j++;k++;
next[j]=k;
//对应字符匹配情况下,s与t指向同步后移
//通过字符串"aaaaab"求next数组过程想一下这一步的意义
//printf("(1) j=%d,k=%d,next[%d]=%d\n",j,k,j,k);
}
else
{
k=next[k];
//我们现在知道next[k]的值代表的是下标为k的字符前面的字符串最长相等前后缀的长度
//也表示该处字符不匹配时应该回溯到的字符的下标
//这个值给k后又进行while循环判断,此时t.data[k]即指最长相等前缀后一个字符**
//为什么要回退此处进行比较,我们往下接着看。其实原理和上面介绍的KMP原理差不多
//printf("(2) k=%d\n",k);
}
}
}
int KMPIndex(SqString s,SqString t) //KMP算法
{
int next[MaxSize],i=0,j=0;
GetNext(t,next);
while (i<s.length && j<t.length)
{
if (j==-1 || s.data[i]==t.data[j])
{
i++;j++; //i,j各增1
}
else j=next[j]; //i不变,j后退,现在知道为什么这样让子串回退了吧
}
if (j>=t.length)
return(i-t.length); //返回匹配模式串的首字符下标
else
return(-1); //返回不匹配标志
}
int main()
{
SqString S,P;
cin>>S.data>>P.data ;
S.length =strlen(S.data );
P.length =strlen(P.data );
int pos=KMPIndex(S,P);
cout<<pos<<endl;
return 0;
}