KMP 字符串匹配算法,用它来实现字符串的匹配,效率很高,特别是当字符串很长的时候。
prefix_table();是一个前缀计算函数。
首先前缀的计算只与模板有关,而与原字符串无关。
如:ABCABDABCAB前缀为
00012012345
第一个A前面没有与它相同的故为0,接下来为AB,A也与B不相同,ABC仍然没有一样的,ABCA,此时最后一个字符A与第一个字符A相同,故为1,
ABCAB,AB重复故为2,依次计算。其实每次计算都是从第一个字符相比较,但是再计算时,假如目前为ABCAB,我们已经知道前面的A匹配了,那我们
只需要比较新加入的字符与第二个字符是否匹配了,如果匹配,那么就加1,如果不匹配则为0.
在写程序时,模板肯定是要传入的,为了后续的使用前缀表,最好也传入一个前缀数组。
void prefix_table(char pattern[] , int prefix[] ,int patternLength)
{
prefix[0] = 0;//前缀的第一个为0
int patternIndex = 1;//因为第一个已经确定了
int prefixPreviesLength = 0; //用来标记当前位置前最大的字串长度
while ( patternIndex < patternLength )
{
if (pattern[patternIndex] == pattern[prefixPreviesLength])
{
prefixPreviesLength++;
prefix[patternIndex] = prefixPreviesLength ;
patternIndex ++;
}
else
{
if( prefixPreviesLength > 0)///保证不越界
prefixPreviesLength = prefix[prefixPreviesLength-1];
//如果不相等则调整,当前位置最大的字符串长度,变为模板的前一个前缀,继续比较
else
{
prefix[patternIndex] = 0; ///已经找不到了,为0
patternIndex++;//计算下一个的前缀
}
}
}
}
一般为了后续程序的书写,将得到的前缀表右移一位,将prefix[0] = -1;
void move_prefix_table(int prefix[] ,int n) { for (int i = n-1 ; i > 0 ; i -- ) { prefix[i] = prefix[i - 1]; } prefix[0] = -1 ; }
准备工作都做好,最后就是与原字符串的匹配了。在匹配前,先完成对前缀表的计算
prefix_table(pattern, prefix ,PatternLength );
move_prefix_table(prefix ,PatternLength );
在匹配过程中,原字符串的索引是一直增加的,不会回溯,这也就是为什么KMP算法相比暴力搜索法高效的原因。
当当前的模板索引与模板长度-1相同时,判断当前的字符与原字符串中的字符是否相等,如果相等,就输出它匹配的位置,
并将前字符的前缀值赋予索引,继续查找,寻找是否还有相匹配的。
void kmp_search(char data[] , char pattern[] ) { int DataLength = strlen(data); int PatternLength = strlen(pattern); int* prefix = new int[PatternLength]; prefix_table(pattern, prefix ,PatternLength ); move_prefix_table(prefix ,PatternLength ); int DataIndex = 0; int PatternIndex = 0; while ( DataIndex < DataLength ) { if (PatternIndex == PatternLength-1 && data[DataIndex] == pattern[PatternIndex]) { printf("Found pattern at %d . " , DataIndex-PatternIndex); PatternIndex = prefix[PatternIndex]; } if ( data[DataIndex] == pattern[PatternIndex]) { DataIndex++; PatternIndex++; } else { PatternIndex = prefix[PatternIndex] ;//回溯查找,回溯到适当的位置 if ( PatternIndex == -1) { PatternIndex++; DataIndex++; } } } }
int main(int argc, char* argv[]) { char data[] = {'a','a','x','a','a','x',' '}; char pattern[] = {'a','a','x',' '}; kmp_search(data ,pattern ); char *data1 = "aaasaadfgdsasvcd"; char *pattern1 = "as"; kmp_search(data1 ,pattern1 ); return 0; }
结果为:
Found pattern at 0 .
Found pattern at 3 .
Found pattern at 2 .
Found pattern at 11 .
注意:若使用的是数组,需要在传递的时候,在数组的最后加上 ,因为strlen只有检测到 才会停止。