KMP算法
KMP字符串查找算法的目的是减少不必要的比较次数,举个简单的例子,从字符串A:”abcdeabcdfg”中查找字符串B:”abcdf”。
使用普通的查找法查找字符串的步骤是这样的:
先拿A[0:4]分别与B对应位置的字母比较,如果不相等则拿A[1:5]与B比较,依次类推,直到结束。
而KMP算法先分析要朝朝的字符串,以B为例,由于a与后面的四个字节都不相同,而在比较A[0:4]与B时在A[4]处发生不匹配现象,所以,再拿A[1:5]、A[2:6]、A[3:7]分别与B比较是没有意义的,直接比较A[4:8]与B即可。这样就省掉了几次循环。
KMP算法在分析检索关键词时需要考虑的问题还有很多,这里就不一一分析了,下面是C实现的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getNext( char* keyword, char* next){
int i = 0, j = -1;
next[0] = -1;
int end_pos = strlen(keyword)-1;
while(i < end_pos){
if(j == -1 || keyword[j] == keyword[i]){
++ i;
++ j;
next[i] = j;
}
else{
j = next[j];
}
}
}
int find( char *string, char *keyword){
int i = 0, j = 0, len = strlen(string), klen = strlen(keyword);
char *next = (char*)malloc( strlen(keyword));
getNext(keyword, next);
while(i < len && j < klen){
if(j == -1 || string[i] == keyword[j]){
++i;
++j;
}
else{
j = next[j];
}
}
free(next);
if(j == klen){
return i-klen;
}
else{
return -1;
}
}
int main(){
char *keyword = "abcdef";
char next[strlen(keyword)];
getNext(keyword, next);
int i;
int len = strlen(keyword);
printf("next:" );
for(i = 0; i != len; ++i){
printf("%d" , next[i]);
}
printf("\n" );
int pos = find("adadababcdefcf", keyword);
printf(" pos = %d\n", pos);
return 0;
}
优化KMP算法
为了更大限度的减少重复匹配次数,还可以对KMP算法进行优化,优化步骤非常简单,下面是优化后的代码,新增的代码后都标有line开头的注释:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getNext( char* keyword, char* next){
int i = 0, j = -1;
next[0] = -1;
int end_pos = strlen(keyword)-1;
while(i < end_pos){
if(j == -1 || keyword[j] == keyword[i]){
++ i;
++ j;
if(keyword[j] == keyword[i]){ // line 1
next[i] = next[j]; // line 2
} // line 3
else{ // line 4
next[i] = j; // line 5
} // line 6
}
else{
j = next[j];
}
}
}
int find( char *string, char *keyword){
int i = 0, j = 0, len = strlen(string), klen = strlen(keyword);
char *next = (char*)malloc( strlen(keyword));
getNext(keyword, next);
while(i < len && j < klen){
if(j == -1 || string[i] == keyword[j]){
++i;
++j;
}
else{
j = next[j];
}
}
free(next);
if(j == klen){
return i-klen;
}
else{
return -1;
}
}
int main(){
char *keyword = "ababa";
char next[strlen(keyword)];
getNext(keyword, next);
int i;
int len = strlen(keyword);
printf("next:" );
for(i = 0; i != len; ++i){
printf("%d" , next[i]);
}
printf("\n" );
int pos = find("abasbabababababababababab", keyword);
printf("pos = %d\n", pos);
return 0;
}
其实,理解起来也比较简单,对于检索关键词K:”ababa”这种情况,假设外层循环计数变量为i,如果在K[0]处发生不匹配,则下次循环前i应该自加,重新与K进行比较,而如果在K[1]处发生不匹配时,则下次循环前,i无需自加,直接与K进行比较,但是如果在K[2]处发生不匹配时,则下次循环前,i应该自加然后与K进行匹配,因为K[2]跟K[0]相等,K[2]与之不匹配则K[0]也与之不匹配,所以i需要自加,从后一个元素开始比较。
本文链接:
http://blog.csdn.net/girlkoo/article/details/17435557
本文作者:girlkoo