KMP算法
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)
#include<string>
#include<iostream>
using namespace std;
//改进后的next数组求解
void get_nextval(string T, int next[])
{
int i = 0;
int j = -1;
next[0] = -1;
int len = T.length();
while (i < len - 1)
{
if (j == -1 || T[i] == T[j])
{
i++;
j++;
if (T[i] != T[j])//当前字符与前缀字符不同
{
next[i] = j; //字符不匹配时回溯的字符位置,next在i的位置为j
}
else
{
next[i] = next[j]; //如果当前字符与前缀字符相同,字符的next值赋值给next在i位置的值
}
}
else
{
j = next[j]; //j值回溯
}
}
}
//标准next求解
void get_next(string T, int next[]) //求模式串T的next数组
{
int len = T.length();
int i = 0;
int j = -1;
next[0] = -1; //第一个字符前无字符串,给值-1
while (i < len-1)
//next数组中i的最大值为T.length -1,对next数组赋值是在i++之后,
//所以while循环的最后i应该为 T.length -2
{
if (j == -1 || T[i] == T[j])
{
i++;
j++;
next[i] = j;
//字符串匹配时,同步后移
}
else
{
j = next[j];
//我们现在知道next[i]的值代表的是下标为i的字符前面的字符串最长相等前后缀的长度
//也表示该处字符不匹配时应该回溯到的字符的下标
//这个值给i后又进行while循环判断,此时T.data[k]即指最长相等前缀后一个字符
}
}
}
//KMP求解 匹配字符串索引位置
int index_kmp(string S, string T, int pos)
{
int j = 0;
int i = pos; //从主串的那一位开始
int next[255];
int lens = S.length();
int lent = T.length();
get_next(T, next); //next = {-1, 0, 0, 1, 2, 3, 1, 1, 2}
//get_nextval(T, next); //next = {-1, 0, -1, 0, -1, 3, 1, 0, -1}
while (i < lens && j < lent)
{
if (j == -1||S[i] == T[j])
{
i++;
j++; //i,j自增
}
else
{
j = next[j]; //i不变,j回溯
}
}
if(j >= lent) // >=
return i - lent; //返回匹配模式串的首字符下标
return -1; //未匹配,返回不匹配标志
}
int main()
{
string a = "ABABAABCDABABAAABAEA";
string b = "ABABAAABA";
cout << index_kmp(a, b, 0) << endl; //KMP算法
system("pause");
return 0;
}
代码注意点:
- 所求解next数组是从-1开始的;
- 求解next数组的while循环判断条件是小于字符长度-1,T.length()-1
- 改进后的求解next数组,在T[i] == T[j]时进行字符回溯
- 求解next时,i表示next的下标,j多数表示next数组的值
- i值一直增加,j值需要回溯