您的位置:首页 > 其它

KMP算法

2015-09-04 21:26 288 查看
当我们在做字符串模式匹配时,如果采用的是笨方法,假设模式串为P = abcabcacab,目标串为babcbabcabcaa,那么我们匹配时是从两者的第一个字符开始匹配,当匹配到某个字符,发现不匹配时,我们会将目标串下标往后移动一位,再次从头开始比较。

KMP算法的特点是,当匹配到某一位发现两者不匹配时,并不将目标串的下标回退到之前开始匹配的下一个,而是将模式串做适当的后移,然后再匹配当时失配的目标串的那一位和移位后的模式串的相应位,如果这次还是失配,那么再次将模式串后移,如果模式串已经是第一位了,那么就将目标串的下标加一,在两个重新开始从模式串的头开始比较。

KMP算法主要就是要根据模式串,求出next[j],这个next[j]是指,当目标串的某一位和模式串比较失配时,下一次目标串的这一位与模式串的next[j]位进行比较。如果这一位再次失配,那就与模式串的next[next[j]]进行比较。所以我们所求出来的next[j]仅仅和模式串有关。

         1 2 3 4 5 6 7 8 9 10

         
a b c a b c a c a b

 next[j]为0 1 1 0 1 1 0 5 0 1

 f[j]   为0 1 1 1 2 3 4 5 1 2

这里我们引入一个概念f(j),其含义是,对于模式串的第j个字符pattern[j],f(j)是所有满足使pattern[1...k-1]
= pattern[j-(k-1)...j - 1](k < j)成立的k的最大值。这里f[i]主要是帮助我们求得next[j].

如果 pattern[j] != pattern[f(j)],next[j] = f(j);

如果 pattern[j] = pattern[f(j)],next[j] = next[f(j)];

现在,我们来总结一下next[j]和f(j)的关系,next[j]是所有满足pattern[1...k - 1] = pattern[(j - (k - 1))...j -1](k < j),且pattern[k]
!= pattern[j]的k中,k的最大值。而f(j)是满足pattern[1...k - 1] = pattern[(j - (k - 1))...j -1](k < j)的k中,k的最大值。还是以上例的模式来说,对于第7个元素,其f(j) = 4, 说明pattern[7]的前3个字符与模式的前缀3相同,但是由于pattern[7]
= pattern[4], 所以next[7] != 4。

也就是说在计算next[j]时,我们已经知道目标串此时已经不匹配了,在计算f[j]时,我们不这样考虑,不管目标串与模式串是否失配。
第一个数字为0,意思是当刚开始就不匹配时,直接将目标串下标加1,然后与模式串第一个比较,与我们以前的笨方法一样。

第二个数字为1,两组数据的第一个数字相同,但是第二个不同时,我们将目标串当前失配元素与模式串的第一个元素比较。

数字为n时,是指失配时,将当前失配元素与模式串的第n个元素比价。next[j]的算法实现。

在此实现中,next数组时从下标一开始有效。pattern也是从下标一开始。

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: