这段时间在看关于kmp算法的内容,其中尤其是matrix67大牛的那篇kmp算法详解,网上评价很高。说实话一开始看没怎么看懂,后面主要结合代码仔细跟了跟, 思路清晰了很多。kmp算法的关键在于利用字符串自身的特点在一处不能匹配后大幅移动,从而将效率降为线性的。
此文并不是从头讲kmp的原理等东西,只是说说自己的一些想法。如果对于kmp想要从头了解的话,可以参考matrix67大牛和网上其他大牛的著作。
kmp的关键在于,预处理 存放当不能匹配时下一个匹配位置的数组。也就是自我匹配的过程,其中P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。
matrix67大牛的代码是
P[1]:=0;
j:=0;
for i:=2 to m do
begin
while (j>0) and (B[j+1]<>B[i]) do j:=P[j];
if B[j+1]=B[i] then j:=j+1;
P[i]:=j;
end;
这是Pascal的代码,将其改成C的便是dst[0] = -1;
j = -1;
for (i = 1; i < strlen(src); i++)
{
while ((j > -1) && (src[j + 1] != src[i]))
{
j = dst[j];
}
if (src[j+ 1] == src[i])
{
j++;
}
dst[i] = j;
}
中间稍作了些处理
如果输入为:
char src[] = "abababacaba";
运行结果为:
src[]: a b a b a b a c a b a
dst[]: -1 -1 0 1 2 3 4 -1 0 1 2
但是中间的-1,0会让人很晕。其实都是需要从头匹配的意思,均可以改成零,这样可以更好得理解。
自己写了个函数可以更方便的用
void kmp_get_next(const char *src, int *dst)
{
int i, j, len_src;
dst[0] = 0;
j = 0;
len_src = strlen(src);
for (i = 1; i < len_src; i++)
{
while ((j > 0) && (src[j] != src[i]))
{
j = dst[j];
}
dst[i] = j;
if (src[j] == src[i])
{
j++;
}
}
return ;
}
运行结果为src[]: a b a b a b a c a b a
dst[]: 0 0 0 1 2 3 4 0 0 1 2
当然,最终的kmp匹配的函数也需要相应修改
int kmp_matching(const char *dst, //待检查的字符串
const char *match_str, //匹配字符串
int *match_arr //预处理数组
)
{
int count = 0; //计数,匹配成功次数
int i, j = 0;
int len_dst = strlen(dst);
int len_match = strlen(match_str);
for (i = 0; i < len_dst; i++)
{
while ((j > 0) && (dst[i] != match_str[j]))
{
j = match_arr[j];
}
if (dst[i] == match_str[j])
{
j++;
}
if (j == len_match)
{
printf("one matching at %d\n", i - len_match + 1);
j = match_arr[j];
count++;
}
}
if (count == 0)
{
printf("no matching\n");
}
return count;
}
此外,生成的预处理数组空间还可以用malloc的方式动态开辟。将这两个函数一封装,就可以成为一个模块了。
总的来说,整体的思想没变,只是对其中稍作修改,便于自己深入理解而已
转贴请注明出处