本博客内容参照http://blog.csdn.net/v_july_v/article/details/7041827
Problem
假设现在我们面临这样一个问题:有一个文本串S,和一个模式串P,现在要查找P在S中的位置,怎么查找呢?
如果用暴力匹配的思路,并假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置,则有:
- 如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;
- 如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。
时间复杂度O((n-m+1)m)
KMP solution
下面先直接给出KMP的算法流程(如果感到一点点不适,没关系,坚持下,稍后会有具体步骤及解释,越往后看越会柳暗花明☺):
假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置
- 如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
- 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。
- 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,即移动的实际位数为:j - next[j],且此值大于等于1。
很快,你也会意识到next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。
此也意味着在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)。如果next [j] 等于0或-1,则跳到模式串的开头字符,若next [j] = k 且 k > 0,代表下次匹配跳到j 之前的某个字符,而不是跳到开头,且具体跳过了k 个字符。
Next数组求解
next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1。意识到了这一点,你会惊呼原来next 数组的求解竟然如此简单:就是找最大对称长度的前缀后缀,然后整体右移一位,初值赋为-1(当然,你也可以直接计算某个字符对应的next值,就是看这个字符之前的字符串中有多大长度的相同前缀后缀)。
Source code
#include <iostream>
using namespace std;
/*
void GetNext(char *p, int *next)
{
next[0] = -1;//初始化
for (int j = 1; p[j] != '\0'; j++)
{
int pre = next[j - 1];
if (p[pre] == p[j-1])//
next[j] = next[j-1] + 1;
else
{
int k = next[j - 1];
while (k != -1&& p[k] != p[j - 1])
{
k = next[k];
if (k == -1)
break;
}
if (k == -1)
next[j] = 0;
else
next[j] = next[k] + 1;
}
}
}
*/
void GetNext(char *p, int *next)
{
int len = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < len)
{
if (k == -1 || p[k] == p[j])
{
k++;
j++;
next[j] = k;
}
else
k = next[k];
}
}
void KMP(char *text, char *p, int *next)
{
int i = 0, j = 0;
bool flag = false;
while (text[i] != '\0')
{
if (text[i] == p[j]||j ==-1)//当前字符匹配成功或者第一个字符不匹配,i,j均向后移动
{
i++;
j++;
}
else
j = next[j];//取下一个匹配地址
if (p[j] == '\0')//匹配成功
{
flag = true;
cout << "Pattern occurs with shift " << i - j << endl;
if (text[i] == '\0')
break;
else
{
j = 0;
i++;
}
}
}
if (!flag)
cout << "Not Matched\n";
}
int main()
{
char *text = new char[100];
char *p = new char[100];
int next[100];//next数组
cout << "Please input source string and objective string\n";
cin >> text >> p;
if (*text == '\0' || *p == '\0')
{
cout << "Empty string!\n";
return 0;
}
GetNext(p, next);
KMP(text, p, next);
return 0;
}