KMP算法是一种查询模式串在主串中第一次出现的位置的算法。比起朴素的BF算法来说,效率高上一点,如果模式串中出现的重复字符多的话,效率会更高。
BF算法的核心思想就是将主串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。
KMP算法的匹配方式和BF算法一致,只是KMP的核心在于减少无意义的回溯,即每一次模式串匹配失败都进行一次最大的跳跃。
KMP算法针对的是模式串,对主串不作处理,KMP算法其实就是实现了一个next()函数,函数返回模式串在每一个下标处出现失配时的回溯指针,即匹配时不回溯主串的当前位置指针,只回溯模式串的位置指针。
我们令模式串的位置指针为j。j的回溯情况有next()函数求出,由模式串的前缀和后缀相似程度决定,我们需要求出最长相等的前缀与后缀的长度,如模式串为(以数组下标表示位置):a b a b a a,那对于下标为2的字符a来说,他的最长相等前后缀长度为0,对于下标为4的a来说他的最长相等前后缀长度为2(a b),同理对于下标为5的a来说,最长相等前后缀长度为3(a b a)。
为什么要确定最长相等的前后缀,这就是KMP算法的具体实现,因为前缀与后缀相等,就不需要按照BF逐步比较,因为主串与模式串S(a b a b a a)如果在S[4]的地方失配,那就说明S[0]到S[3]和主串是一致的,而且我们发现S[0] == S[2],S[1] == S[3],我们就可以直接将匹配位置跳转到S[2],因为主串在对应S[2]与S[3]地方一定和S[0]与S[1]对应。
下面是next()的具体实现:
void get_next(String& str,int (&next)[255])
{
int prefix,postfix;
next[0] = -1;
prefix = 0;
postfix = 1;
while(postfix != str.length)
{
if(str.str[0] == str.str[1])
{
next[1] = -1;
}
else
{
next[1] = 0;
}
if(prefix == -1 || str.str[prefix] == str.str[postfix])
{
prefix++;
postfix++;
if(str.str[prefix] != str.str[postfix])
{
next[postfix] = prefix;
}
else
{
next[postfix] = next[prefix];
}
}
else
{
prefix = next[prefix];
}
}
}
想要理解KMP算法,要对理解对于模式串来说,前缀是固定的,后缀是浮动的,前缀的下标的意义是当前匹配的最长相等前后缀的长度,一旦发生前后缀失配,我们需要回溯的就是前缀,继续寻找当前匹配的最长相等前后缀长度,我们需要利用的就是前面求出的next数组,要理解next数组的含义:模式串在每一个下标处出现失配时的回溯指针。