KMP
KMP
是一种改进的模式匹配算法,它与朴素的模式匹配算法的区别在于:
每当匹配过程中出现比较字符不相等时,不需要回退主串的字符位置指针,而是利用已经得到的部分匹配
结果将模式串向右滑动
尽可能远的距离,再继续进行比较。
KMP匹配算法的思想
设模式串为p0 … pm-1,当模式串中的字符pj与主串中相应的字符Si不相等时,因其前j个字符p0 … pj-1已经获得了成功的匹配,若模式串的p0 … pk-1与pj-k … pj-1相同,这时可令Pk与Si进行比较,从而使i无须回退。
在KMP
算法中,依据模式串的next
函数值实现子串的滑动。若令next[j]=k
,则next[j]
表示当模式串中的Pj与主串中相应字符不相等时,令模式串的Pnext[j]与主串的相应字符进行比较。
next函数的定义如下:
N e x t [ j ] = { − 1 , 当 j = 0 时 m a x ( K ) , { K ∣ 0 < K < j 且 P 0... P k − 1 = P j − k . . . P j − 1 } 0 , 其 它 情 况 Next[j] = \begin{cases} -1,\,\,当j=0时\\ max(K),\,\,\{K|0<K<j且P0...Pk-1=Pj-k...Pj-1\}\\ 0,\,\,其它情况\\ \end{cases} Next[j]=⎩⎪⎨⎪⎧−1,当j=0时max(K),{K∣0<K<j且P0...Pk−1=Pj−k...Pj−1}0,其它情况
示例代码
#include <iostream>
#include <string>
using namespace std;
// 求模式串p的next函数值,并存入数组next,要求next容量>=len(p)
void GetNext(const string &p, int next[])
{
int i = 0;
int j = -1;
next[0] = -1;
int len = p.length();
while (i < len) {
if (j == -1 || p[i] == p[j]) {
++i;
++j;
next[i] = j;
} else {
j = next[j];
}
}
}
/*
* 利用模式串p的next函数,求p在主串s中第一次出现的位置
* 若匹配成功,返回模式串在主中的位置(下标),否则返回-1
* */
int KMP(const string &s, const string &p, int next[])
{
int i = 0;
int j = -1;
int sLen = s.length();
int pLen = p.length();
GetNext(p, next);
while (i < sLen && j < pLen) {
if (j == -1 || s[i] == p[j]) {
++i;
++j;
} else {
j = next[j];
}
}
if (j >= pLen) {
return i - pLen;
}
return -1;
}
int main() {
string str = "abcccddddabcdabcd";
string p = "abcd";
int next[100];
cout << KMP(str, p, next);
return 0;
}
计算字符串中子串出现的次数与位置
/*
只需要在KMP中添加一个判断,当找到子串后,输出即可
*/
void KMP(const string &s, const string &p, int next[])
{
int i = 0;
int j = -1;
int count = 0;
int sLen = s.length();
int pLen = p.length();
GetNext(p, next);
while (i < sLen && j < pLen) {
if (j == -1 || s[i] == p[j]) {
++i;
++j;
} else {
j = next[j];
}
// 当满足条件时,输出次数与位置
if (j == pLen) {
j = 0;
count++;
cout << "count:" << count << " position:" << i-pLen+1 << endl;
}
}
}