KMP算法
KMP算法比较抽象,第一次接触的人往往晦涩难懂,博主也是前前后后好几次思索才有了些许的思路,故在此及时记录,防备遗忘。
KMP算法对于朴素匹配算法的改进是引入了一个跳转表next[]。以模式字符串abcabcacab为例,其跳转表为:
j 1 2 3 4 5 6 7 8 9 10
pattern[j] a b c a b c a c a b
next[j] 0 1 1 0 1 1 0 5 0 1
跳转表的用途是,当目标串target中的某个子部target[m…m+(i-1)]与pattern串的前i个字符pattern[1…i]相匹配时,如果target[m+i]与pattern[i+1]匹配失败,程序不会像朴素匹配算法那样,将pattern[1]与target[m+1]对其,然后由target[m+1]向后逐一进行匹配,而是会将模式串向后移动i+1 - next[i+1]个字符,使得pattern[next[i+1]]与target[m+i]对齐,然后再由target[m+i]向后与依次执行匹配。
匹配过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
b a b c b a b c a b c a a b c a b c a b c a c a b c
a b c a b c a c a b
a b c a b c a c a b
a b c a b c a c a b
a b c a b c a c a b
a b c a b c a c a b
a b c a b c a c a b
next跳转表,在进行模式匹配,实现模式串向后移动的过程中,发挥了重要作用。这个表看似神奇,实际从原理上讲并不复杂,对于模式串而言,其前缀字符串,有可能也是模式串中的非前缀子串,这个问题我称之为前缀包含问题。以模式串abcabcacab为例,其前缀4 abca,正好也是模式串的一个子串abc(abca)cab,所以当目标串与模式串执行匹配的过程中,如果直到第8个字符才匹配失败,同时也意味着目标串当前字符之前的4个字符,与模式串的前4个字符是相同的,所以当模式串向后移动的时候,可以直接将模式串的第5个字符与当前字符对齐,执行比较,这样就实现了模式串一次性向前跳跃多个字符。所以next表的关键就是解决模式串的前缀包含。当然为了保证程序的正确性,对于next表的值,还有一些限制条件,后面会逐一说明。
跳转表的建立
`inline void BuildNext(const char* pattern, size_t length, unsigned int* next)
{
unsigned int i, t;
i = 1;
t = 0;
next[1] = 0;
while(i < length + 1)
{
while(t > 0 && pattern[i - 1] != pattern[t - 1])
{
t = next[t];
}
++t;
++i;
if(pattern[i - 1] == pattern[t - 1])
{
next[i] = next[t];
}
else
{
next[i] = t;
}
}
//pattern末尾的结束符控制,用于寻找目标字符串中的所有匹配结果用
while(t > 0 && pattern[i - 1] != pattern[t - 1])
{
t = next[t];
}
++t;
++i;
next[i] = t;
} `
利用跳转表实现字符串匹配的算法如下:
unsigned int KMP(const char* text, size_t text_length, const char* pattern, size_t pattern_length, unsigned int* matches)
{
unsigned int i, j, n;
unsigned int next[pattern_length + 2];
BuildNext(pattern, pattern_length, next);
i = 0;
j = 1;
n = 0;
while(pattern_length + 1 - j <= text_length - i)
{
if(text[i] == pattern[j - 1])
{
++i;
++j;
//发现匹配结果,将匹配子串的位置,加入结果
if(j == pattern_length + 1)
{
matches[n++] = i - pattern_length;
j = next[j];
}
}
else
{
j = next[j];
if(j == 0)
{
++i;
++j;
}
}
}
//返回发现的匹配数
return n;
}
【参考文献】
《Introduction to Algorithms》Second Edition
by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford .
推荐 “Bill_Hoo专栏” 博客,更多详细内容,可阅读http://billhoo.blog.51cto.com/2337751/411486