KMP算法:最简单的字符串匹配算法由于需要回溯查找,时间复杂度大。而改进型的KMP算法充分利用字符串的特性,减少了主字符串回溯需要查找的时间,大大的降低了其复杂度,大小为O(n+m),n为主字符串长度,m为从字符串长度。
下面介绍下KMP算法的步骤:
主字符串:aabbdcacaabca 从字符串:aabca
首先要设定指向主和从的指针i,j.KMP算法规定主指针不能回头,即网前移动后不能倒回去寻找匹配字符,那么就根据这个规定我们看下有什么变化和要求。
1.首先aabbdcacaabca的第一位和aabca的第一位比较,如果相等i和j都跳到下一位比较,如果不等,i不变j调到前一位比较直到找到相等的。
i=1 |
|
|
|
|
|
|
|
|
|
|
|
|
a | a | b | b | d | c | a | c | a | a | b | c | a |
a |
|
|
|
|
|
|
|
|
|
|
|
|
j=1 |
|
|
|
|
|
|
|
|
|
|
|
|
2.同步骤1,比较了i=2和i=3
|
| i=3 |
|
|
|
|
|
|
|
|
|
|
a | a | b | b | d | c | a | c | a | a | b | c | a |
a | a | b |
|
|
|
|
|
|
|
|
|
|
|
| j=3 |
|
|
|
|
|
|
|
|
|
|
3.当i=3时发现c!=b,这时让从串回溯,看3.1后的演示:
|
|
| i=4 |
|
|
|
|
|
|
|
|
|
a | a | b | b | d | c | a | c | a | a | b | c | a |
a | a | b | c!=b |
|
|
|
|
|
|
|
|
|
|
|
| j=4 |
|
|
|
|
|
|
|
|
|
3.1由于i不能动,现在我们也不知道j应该在哪个值可行,那只有一个一个试。
考虑从j-1位置依次往从字符串首靠比较合理。发先j=2处,a != b,因为只要一个不
匹配,说明主串对应的段绝对不会与字串匹配。因此移动j-1位继续移动。
|
|
| i=4 |
|
|
|
|
|
|
|
|
|
a | a | b | b | d | c | a | c | a | a | b | c | a |
| a | a | b |
|
|
|
|
|
|
|
|
|
|
|
| j=3 |
|
|
|
|
|
|
|
|
|
3.2这时j=2,发现也不匹配。
|
|
| i=4 |
|
|
|
|
|
|
|
|
|
a | a | b | b | d | c | a | c | a | a | b | c | a |
|
| a | a |
|
|
|
|
|
|
|
|
|
|
|
| j=2 |
|
|
|
|
|
|
|
|
|
3.3这时j=1,发现也不匹配,现在的从串数据都比较完了,
那么这时i和j都跳到下一位比较。
|
|
| i=4 |
|
|
|
|
|
|
|
|
|
a | a | b | b | d | c | a | c | a | a | b | c | a |
|
|
| a |
|
|
|
|
|
|
|
|
|
|
|
| j=1 |
|
|
|
|
|
|
|
|
|
4这时两者有从头开始比较
|
|
|
| i=5 |
|
|
|
|
|
|
|
|
a | a | b | b | d | c | a | c | a | a | b | c | a |
|
|
|
| a |
|
|
|
|
|
|
|
|
|
|
|
| j=1 |
|
|
|
|
|
|
|
|
从上面3发现 c!=b后,有没有未卜先知的功能让从字符串的比较一下子
跳到步骤4,而省去3.1,3.2和3.3这样的累赘,不就轻松很多。呵呵,
(^_^)我们的大师K,M和P就想出了省力讨好偷懒的办法
这个值怎么算到让j一下子跳到哪个位置还是有点复杂。
1. 首先规定第一个位置为0,0代表的意思是在这个位置的从字符和主字符串都
不匹配,两者都跳到下一个位置达到从来!比如从字符串的第一个a,如果主不是的a话那么他们都跳到下一个位置比较。
当next大于0时,它的意义就是,主字符串的指针不变,从的字符串调到对应的next值位与主字符串现在的位置做比较。比如主是aadcef,从是aabca, 从的next值是aabca.其中从b的对应next值为2,也就是说当比较到b这位不匹配时,从应回溯到从2对应的位置上与主进行比较,即从的第二个a上面。
主 | b |
|
|
|
|
从 | a | a | b | c | a |
| 1 |
|
|
|
|
next | 0 |
|
|
|
|
2. 第1位匹配,假如第2位不匹配时,发现第1位a和
第2位a一样的值,那么next也一样。
主 | a | b |
|
|
|
从 | a | a | b | c | a |
|
| 2 |
|
|
|
next | 0 | 0 |
|
|
|
3. 如果第3位不匹配,因为2位和3位不一样,当3不行的时候,可以把
2位移过来,且第1位与主字符串完全匹配。
主 | a | a | c |
|
|
从 | a | a | b | c | a |
|
|
| 3 |
|
|
next | 0 | 0 | 2 |
|
|
4.第4位不匹配时,同上可得。
主 | a | a | b | b |
|
从 | a | a | b | c | a |
|
|
|
| 4 |
|
next | 0 | 0 | 2 | 1 |
|
5.第5位不匹配时,按同样的方法。
主 | a | a | b | c | c |
从 | a | a | b | c | a |
|
|
|
|
| 5 |
next | 0 | 0 | 2 | 1 | 0 |
说个一般性规律,还真不好说,我现在只是意会到了,言传太难了!!!
详细请看源代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> //计算nextValue的值 //其中不明白的地方是 int getNext( const char t[], int next[] ) { unsigned int i,j; i = 1; next[1] = 0; //第一个必定为0 j = 0; while( i < ( strlen( t )+1 ) ) { if( j==0 || ( ( j > 0 ) && ( t[i-1] == t[j-1] ) ) ) { i++; j++; if( t[i-1] != t[j-1] ) { next[i] = j; } else { next[i] = next[j]; } } else { j = next[j]; } } return 1; } int main( void ) { char *s = “aavnsghsabc”; //随机设置字符串 char *t = “abaabcac”; int t_len, s_len; int pos=-1; int i,j,k; int *next; t_len = strlen( t ); s_len = strlen( s ); next = ( int * )malloc( sizeof( int ) * ( t_len+2 ) ); getNext( t, next ); //计算出从字符串的nextValue值 for( j = 0; j < t_len+1; j++ ) next[j] = next[j+1]; //将nextValue值平移到从数组0开始位置 i = 0; j = 0; while( i < (s_len+1 ) && j < ( t_len+1 ) ) {//寻找相匹配的位置 if( s[i] == t[j] ) { i++; j++; } else { k = next[j]; if( k == 0 ) { j++; i++; } else { j = k – 1; } } if( t[j] == ‘/0’ ) pos = i – j + 1; } if( pos > 0 ) { printf( “在%d的个位置找到相同的字符串/n”, pos ); } else { printf( “没有找到相匹配的字符串/n” ); } if( next ) free( next ); return 0; }