现总结如下:
想理解KMP算法就要先看看最原始的模式匹配
由于不必要的回溯增加了算法本身的时间复杂度,但完全不回溯就有可能漏了一个可能匹配成功的情况。
比如 : string = aaabababcaaadfe 不回溯就是这样 aaabababcaaadfe
pat = ababc //失配 ababc
所以如果完全不回溯就会漏了这个情况 aaabababcaaadfe
ababc //匹配成功
为什么会漏了那个情况呢?
其实这就是KMP研究的问题,是由于pat = ababc本身性质所决定,因为pat本身有重合的地方
那么KMP是怎么做的呢?
KMP是回溯pat串,而string串始终不回溯,这样的部分回溯就使得算法本身效率提高了很多,特别当string很长时
现在我们就可以来研究一下KMP的算法
我们想如果对于上面的例子,让j -= 2;i不变,是不是就是漏了的那种情况呢。显然这是对的,这样再继续下面的匹配函数过程,就不会有问题了。这就是KMP的算法所在。那怎么处理才知道什么时候让j减几呢?这就是KMP定义的failure function.
令P = P 0P 1P...P n-1是一个模式,则其失配函数f定义为:
f(j) = i 为满足 i < j且使得 P 0P 1...P i = P j-iP j-i+1...P j的最大整数 如果i >= 0
f(j) = -1
例如,对于模式pat = abcabcacab,有:
j 0 1 2 3 4 5 6 7 8 9
pat a b c a b c a c a b
f(j) -1 -1 -1 0 1 2 3 -1 0 1
呵呵,是不是有点晕了,我看到这也晕了。没关系,继续,go ahead! Life will be better!比比先前我的那个想法,就会明白一些,也就是说,如果比较到了j = 6的位置,出现了问题,那么我们完全可以从j = 3的位置开始比较,也就是pat abcabcacab回溯了3个位置,而string当前i的位往前数3个位置肯定是abc。是不是明白了一些。
Anyhow,让我们先实现这个failure function再说
现在再看定义了failure function有什么用
当然是得到模式匹配的规则:如果出现了形如S i-j...S i-1 = P 0P 1...P j-1且S i != P j的部分匹配,那么,若j != 0, 则下一趟模式匹配时,从失配字符S i和模式串字符P f(j-1)+1处重新开始比较;而若j = 0时,则继续比较S i+1和P 0
现在实现模式匹配算法pmatch()
现在是不是很佩服D.E.Knuth,J.H.Morris和V.R.Pratt他们啊,其实很简单的原理,但就是他们才总结出来。
创新才是最难的!
想理解KMP算法就要先看看最原始的模式匹配
int
match(
char
*
string
,
char
*
pat)
//
原模式匹配函数
{
int i = 0, j = 0;
while (i < strlen(string) && j < strlen(pat)) {
if (sting[i] == pat[j]) {
i++;
j++;
} else {
i = i - j + 1; //对string回溯
}
}
if (j = strlen(pat))
return i - strlen(pat);
else
return 0;
}
{
int i = 0, j = 0;
while (i < strlen(string) && j < strlen(pat)) {
if (sting[i] == pat[j]) {
i++;
j++;
} else {
i = i - j + 1; //对string回溯
}
}
if (j = strlen(pat))
return i - strlen(pat);
else
return 0;
}
由于不必要的回溯增加了算法本身的时间复杂度,但完全不回溯就有可能漏了一个可能匹配成功的情况。
比如 : string = aaabababcaaadfe 不回溯就是这样 aaabababcaaadfe
pat = ababc //失配 ababc
所以如果完全不回溯就会漏了这个情况 aaabababcaaadfe
ababc //匹配成功
为什么会漏了那个情况呢?
其实这就是KMP研究的问题,是由于pat = ababc本身性质所决定,因为pat本身有重合的地方
那么KMP是怎么做的呢?
KMP是回溯pat串,而string串始终不回溯,这样的部分回溯就使得算法本身效率提高了很多,特别当string很长时
现在我们就可以来研究一下KMP的算法
我们想如果对于上面的例子,让j -= 2;i不变,是不是就是漏了的那种情况呢。显然这是对的,这样再继续下面的匹配函数过程,就不会有问题了。这就是KMP的算法所在。那怎么处理才知道什么时候让j减几呢?这就是KMP定义的failure function.
令P = P 0P 1P...P n-1是一个模式,则其失配函数f定义为:
f(j) = i 为满足 i < j且使得 P 0P 1...P i = P j-iP j-i+1...P j的最大整数 如果i >= 0
f(j) = -1
例如,对于模式pat = abcabcacab,有:
j 0 1 2 3 4 5 6 7 8 9
pat a b c a b c a c a b
f(j) -1 -1 -1 0 1 2 3 -1 0 1
呵呵,是不是有点晕了,我看到这也晕了。没关系,继续,go ahead! Life will be better!比比先前我的那个想法,就会明白一些,也就是说,如果比较到了j = 6的位置,出现了问题,那么我们完全可以从j = 3的位置开始比较,也就是pat abcabcacab回溯了3个位置,而string当前i的位往前数3个位置肯定是abc。是不是明白了一些。
Anyhow,让我们先实现这个failure function再说
/*author: livahu
**2006.11.26
**/
void fail( char * pat, int * failure) {
int i, j;
int n = strlen(pat);
failure[0] = -1; //定义最初情况
for (j = 1; j < n; j++) {
i = failure[j - 1];
while ((pat[j] != pat[i + 1]) && (i >= 0))
i = failure[i];
if (pat[j] == pat[i + 1])
failure[j] = i + 1;
else
failure[j] = -1; //代表没有重合的,不能形成重合子串
}
}
**2006.11.26
**/
void fail( char * pat, int * failure) {
int i, j;
int n = strlen(pat);
failure[0] = -1; //定义最初情况
for (j = 1; j < n; j++) {
i = failure[j - 1];
while ((pat[j] != pat[i + 1]) && (i >= 0))
i = failure[i];
if (pat[j] == pat[i + 1])
failure[j] = i + 1;
else
failure[j] = -1; //代表没有重合的,不能形成重合子串
}
}
当然是得到模式匹配的规则:如果出现了形如S i-j...S i-1 = P 0P 1...P j-1且S i != P j的部分匹配,那么,若j != 0, 则下一趟模式匹配时,从失配字符S i和模式串字符P f(j-1)+1处重新开始比较;而若j = 0时,则继续比较S i+1和P 0
现在实现模式匹配算法pmatch()
/*author: livahu
**2006.11.26
*/
include < stdio.h >
#include < string .h >
#define MAX_STRING_SIZE 100
#define MAX_PAT_SIZE 100
int pmatch( char * string , char * pat)
{
int failure[MAX_PAT_SIZE];
fail(pat, failure); //得到对于pat不同位置的f函数值
int i = 0, j = 0;
int lens = strlen(string);
int lenp = strlen(pat);
while (i < lens && j < lenp) {
if (string[i] == pat[j]) {
i++;
j++;
} else if (j == 0) { //string字符串往后移
i++;
} else {
j = failure[j - 1] + 1; //使模式串字符转到Pf(j-1)+1
}
}
return ((j == lenp) ? (i - lenp) : -1); //判断是否成功匹配
}
**2006.11.26
*/
include < stdio.h >
#include < string .h >
#define MAX_STRING_SIZE 100
#define MAX_PAT_SIZE 100
int pmatch( char * string , char * pat)
{
int failure[MAX_PAT_SIZE];
fail(pat, failure); //得到对于pat不同位置的f函数值
int i = 0, j = 0;
int lens = strlen(string);
int lenp = strlen(pat);
while (i < lens && j < lenp) {
if (string[i] == pat[j]) {
i++;
j++;
} else if (j == 0) { //string字符串往后移
i++;
} else {
j = failure[j - 1] + 1; //使模式串字符转到Pf(j-1)+1
}
}
return ((j == lenp) ? (i - lenp) : -1); //判断是否成功匹配
}
创新才是最难的!