KMP算法:KMP是由3个外国人想出来的设计的线性时间字符串匹配算法。时间复杂度很低O(n),是判定字串的一个十分简便的方法。KMP算法在搜索发生失配时,将模式串向右滑动到某个位置重新开始匹配而不是回溯,提高了搜索效率。经过分析,KMP算法的时间复杂度为O(m+n),暴力搜索算法的时间复杂度为O(mn),KMP算法的搜索效率远远高于暴力搜索算法。
我们采用了一个next数组来解决该问题。
KMP算法的关键所在就是求出字符串的next数组。
方法:
next = 前后缀最长公共部分+1
因为最长公共部分是已经匹配了的,所以要从公共部分的下一个开始比较,而next的值就是下一次比较时开始的位置.
这里需要注意两点,
一.待匹配串的第一个字符是不需要回退的,因为它就是回退的最后一个了.
二.子串前后缀没有重合部分且子串不为空,即需要从子串头开始匹配,所以next=1.
a b a b a b b
对于这个字符串,来对它进行求next
当j=1时发生不匹配,子串为空,next = 0
当j=2时发生不匹配,子串为 A,前后缀公共部分是空, next = 1
当j=3时发生不匹配,子串为 AB,前后缀公共部分为空, next = 1,
当j=4时发生不匹配,子串为 ABA,前后缀公共部分是A, next = 2
当j=5时发生不匹配,子串为 ABAB,前后缀公共部分是AB, next = 3
当j=6时发生不匹配,子串为 ABABA,前后缀公共部分是ABA, next = 4
当j=7时发生不匹配,子串为 ABABAB,前后缀公共部分是ABAB, next = 5
最终得到next数组的值
next数组如何求出依靠这个公式
//求出next数组
void getNext(char arr[], int next[])
{
int i = 1;
int j = 0;
next[1] = 0;
while (i < (int)strlen(arr))
{
if (j == 0 || arr[i] == arr[j])
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
}
kmp算法的改进:
上述根据next数组去查询子串还是有缺陷的,如果主串为“aaaabcde”,子串为“aaaaax”的话,子串的next数组值为“012345”在开始匹配时发现当i=5,j=5,时b与a不相等。
因此j=next[5]=4,如图。
此时a与b 仍不相等。j=next[4]=3;如图
一直到j=0时,根据算法重新算出i=6,j=1.如图
去进行下次匹配,我们发现效率还是比较低。中间还有其他多余的判断。于是我们对算法进行了改进。
给出 一个字符串S“abababcab'',和子串“ababc”.
现在我们重新定义一个新的next数组,把子串的最小前后缀的数字存放到next数组中。
子串“ababc”的子串有a,ab,aba,abab.四个。
子串“ababc”的前缀有四个a,ab,aba,abab.后缀也有四个b,ba,bab,babc.
接下来我们求出子字符串的最长前后缀。
a 没有最长前后缀,为0.
ab,前缀为a,后缀为b。前后缀不相等。为0.
aba,前缀为a,ab后缀为a,ba。最小前缀相等。为1
abab,前缀为a,ab,aba.后缀为b,ab,bab。ab相等。为2
将这四个数字组成一个数组,数组的第一位是-1,这样就构成了新的next数组{-1,0,0,1,2}。
接下来我们在字符串S中进行查找子串。
当匹配到第四个数字时发现不相等,这时候C处的next数组值为2.
那么将下面的字符串重新匹配将数组下标为2的位置重新放到星星标记的位置。
这样就匹配成功了。
#include<iostream>
using namespace std;
//next数组
void Get_Next(char* T, int *next)
{
int j = 0;
int k = -1;
next[0] = -1;
while (j < strlen(T))
{
if (k == -1 || T[j] == T[k])
{
j++;
k++;
next[j] = k;
}
else
{
k = next[k];
}
}
}
int KMP(char* S, char* T, int *next)
{
int i = 0, j = 0;
Get_Next(T, next);
int slen = strlen(S);
int tlen = strlen(T);
while (i < slen && j < tlen)
{
if (j == -1 || S[i] == T[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j == tlen)
{
return i - tlen;
}
else
{
return -1;
}
}
int main()
{
cout << "请输入一串字符和要查找的字符串:" << endl;
char S[32];
char T[32];
cin >> S >> T;
int next[32];
Get_Next(T, next);
cout << "next数组为:";
for (int i = 0; i < strlen(T); i++)
{
printf("%d ", next[i]);
}
cout << endl;
cout << "匹配出现的位置:" << KMP(S, T, next) << endl;
return 0;
}