朴素的匹配算法
简单来说就是:从主串s 和子串target 的第一个字符开始,将两字符串的字符一一比对,如果出现某个字符不匹配,主串将回溯到开始字符的下一个字符,子串回溯到第一个字符再进行一一比对。如果出现某个字符不匹配,继续上述循环,一直到子串字符全部匹配成功。这种就是我们的匹配算法,简单但是效率低。 低效的原因:回溯的步骤太多,没有充分利用已经匹配的信息。
KMP算法
实质就是解决朴素的匹配算法的低效的原因。
使用前后缀利用已经匹配的信息,如下 主串s: ababbaabc 子串target: ababc 当发现不匹配时,使子串移动最大已匹配子串的最长个相同前后缀的长度。上述例子已匹配子串为abab,它的最长个相同前后缀长度为2。
ababbaabc
---ababc
所以KMP算法最重要的部分就是获取子串的最长前后缀的长度,也就是GetNext函数。如下
void GetNext(string str, int next[])
{
next[0] = -1;
int len = str.size();
int first = -1, last = 0;
while (last < len)
{
if (first == -1 || str[first] == str[last])//求last+1
{
cout << " ss " << " ";
first++, last++;
//abcaabXacf abcaabXacf
if (str[first] == str[last])//abcaabc(模式串) 如果不进行这步判断时,说明此时为移位如下 abcaabc
{ //可知为无效移位,正确移动位abcaabXacf,即取next[next[last]],也就是next[first]的前后缀长度
//abcaabc
next[last] = next[first];
}
else
{
next[last] = first;
}
}
else
{
first = next[first];
//我们现在知道next[first]的值代表的是下标为k的字符前面的字符串最长相等前后缀的长度
//也表示该处字符不匹配时应该回溯到的字符的下标
// str[first] != str[last],first应该返回next[first],如下
//abacabaa 当first=2,last=7,first=next[2]处
}
}
}
int KPM(string give,string target)
{
int len_g = give.size();
int len_t = target.size();
int* next = new int[len_t+1];
GetNext(target, next);
int g = 0, t = 0;
while (g<len_g&&t<len_t)
{
if (t== -1 || give[g]==target[t])
{
g++; t++;
}
else
{
t= next[t];
}
}
if (t>=len_t)
{
return g - t;
}
return -1;
}
int main()
{
cout<< KPM("abcabadsfdfddg", "abad");
}