原理详解
KMP算法的作用是在一个已知字符串中查找子串的位置,也叫做串的模式匹配。比如主串s=“goodgoogle”,子串t=“google”。现在我们要找到子串t 在主串s 中的位置。
第一种我们容易想到的就是暴力求解法。
这种方法也叫朴素的模式匹配:
简单来说就是:从主串s 和子串t 的第一个字符开始,将两字符串的字符一一比对,如果出现某个字符不匹配,主串回溯到第二个字符,子串回溯到第一个字符再进行一一比对。如果出现某个字符不匹配,主串回溯到第三个字符,子串回溯到第一个字符再进行一一比对一直到子串字符全部匹配成功。
这种算法在最好情况下时间复杂度为O(n)。即子串的n个字符正好等于主串的前n个字符,而最坏的情况下时间复杂度为O(m*n)。相比而言这种算法空间复杂度为O(1),即不消耗空间而消耗时间。
详细原理参考:KMP算法配图详解
对于nextval矩阵,如果第a位字符与它next值指向的第b位字符相等,则该a位的nextval就指向b位的nextval值,如果不等,则该a位的nextval值就是它自己a位的next值。
c++代码实现
#include<iostream>
#include<vector>
using namespace std;
class KMP {
public:
void Next(string data,vector<int>&next) {
int i = 0, j = -1;
next[0] = -1;//next数组第一个值为-1
while (i < data.size()-1) {
if (j == -1 || data[i] == data[j]) {
i++;
j++;
next[i] = j;
}
else j = next[j];
}
}
void NextVal(string data, vector<int>& next) {
int i = 0, j = -1;
next[0] = -1;
while (i < data.size() - 1) {
if (j == -1 || data[i] == data[j]) {
i++;
j++;
if (data[i] != data[j])next[i] = j;
else next[i] = next[j];
}
else j = next[j];
}
}
int kmpFind(string data,string son) {
int idx_data = 0;//data的当前下标
int idx_son = -1;//son的当前下标
vector<int> next(son.size(), 0);//next数组
//this->Next(son, next);//求得next数组
this->NextVal(son, next);
//string.size()是unsigned int类型,直接判断会导致(int)-1 > (unsigned)3
while (idx_data < (int)data.size() && idx_son < (int)son.size()) {
//两字符相等时继续
if (idx_son == -1 || data[idx_data] == son[idx_son]) {
idx_data++;
idx_son++;
}
//指针后退开始重新匹配,j回退到合适位置,i不需要动
else idx_son = next[idx_son];
}
if (idx_son >= son.size()) return idx_data - son.size();
else return 0;
}
};
int main() {
KMP k;
string s1 = "dsadsadsaadsadsa";
string son = "dsa";
int a = k.kmpFind(s1, son);
cout << a << endl;
return 0;
}