昨天看了一下用于字符子串查找的KMP算法,很巧妙,也很难编,程序不长,理解起来可真是费劲。因为要用C语言来实现,所以书里KMP算法定义的next数组的求值方法需要改动,因为C语言里的字符串数据结构并不是让第一位存储字符串长度,而是直接保存了第一个字符,就是这样一个小小的变化,可让我伤透了脑筋,而且即便如此,对于算法的本质也不算了解得很透彻,几乎是在死命调试的情况下把程序给实现出来,里面是不是有bug也不敢确定,编程可真是件纠结的事,要是早些接触就好了,可以静静的思考,不用背负着找工作的压力来学习。
寻找字符串的子串,结果返回第一个匹配的子串首字符的位置,从0开始。
1.先从简单算法开始,简单算法主要是通过循环来实现。
int Index( char* s, char* t, int pos ) /*s为目标字符串,t为要寻找的字串,pos为s中的开始位置*/
{
int slen, tlen;
int i = pos, j = 0; /*使用i标记s中的位置,使用j标记t中的位置*/
if( s == NULL || t == NULL )
return -1;
slen = strlen( s );
tlen = strlen( t );
if( tlen > slen || pos < 0 || pos > tlen - 1 )
return -1;
while( i < slen && j < tlen )
{
if( s[i] == t[j] )
{ ++i; ++j }
else
{ i = i - j + 1; j = 0; } /*通过回溯i指针并将j重新指向起始位置*/
}
if( j >= tlen )
return i - j;
else
return -1;
}
2.首位匹配算法
类似简单算法,只是要先比较模式串t的首尾字符,首尾字符比较完后,再应用简单算法比较中间字符
int Index_FL( char* s, char* t, int pos )
{
int slen, tlen, patStart, patEnd;
int i = pos, j, k;
if( s == NULL || t == NULL )
return -1;
slen = strlen( s );
tlen = strlen( t );
if( tlen > slen || pos < 0 || pos > tlen - 1 )
return -1;
patStart = t[0];
patEnd = t[ tlen - 1 ];
while( i <= slen - tlen ) /*最后一个匹配位置为i = slen - tlen*/
{
if( s[i] != patStart ) ++i;
else if( s[ i + tlen - 1 ] != patEnd ) ++i;
else
{
k = 1; j = 1; /*用k来标记s中的位置, 以i + 1为起点*/
while( j < tlen - 1 && s[ i + k ] == t[j] ) /*对中间字符串应用简单算法进行匹配*/
{ j++; k++; }
if( j >= tlen - 1 ) /*由于末尾已经检测过相等,所以当 j = tlen - 1时表明匹配*/
return i;
else /*不匹配则i右移一位*/
i++;
}
}
return -1;
}
3.KMP算法,主要差别就在于,之前在遇到不匹配的情况时都是回溯s字符串的i指针,使用该算法则保持i不变,回溯t字符串的j指针,并且通过将t数组的首尾字串的重合位数记录在next数组中,在匹配过程中,通过next数组的值,来确定j指针的回溯位置。(说的比较抽象,具体学习还是参看教材,我这里就是做个记录便于以后复习或改进O(∩_∩)O)
红色部分是跟原先算法主要的不同之处
void get_next( char* t, int next[] ) /*若不存在k使得‘p0...pk-1’ = ‘pj-k...pj-1’,则令next[j] = 0(原先算法是令这种情况的next值为1)*/
{
int tlen;
int i = 1, j = 0;
next[0] = 0;
next[1] = 0;
tlen = strlen( t );
while( i < tlen )
{
if( t[i] == t[j] )
{ ++i; ++j; next[i] = j; }
else
{
j = next[j];
if( j == 0 ) /*j == 0则代表next[i + 1] = 0,即不存在k使得‘p0...pk’ = ‘pi-k...pi’*/
next[++i] = 0;
}
}
}
int Index_KMP( char* s, char* t, int pos )
{
int slen, tlen, i, j;
int next[100];
if( s == NULL || t == NULL )
return -1;
slen = strlen( s );
tlen = strlen( t );
get_next( t, next );
if( tlen > slen || pos < 0 || pos > tlen - 1 )
return -1;
i = pos; j = 0;
while( i < slen && j < tlen )
{
if( s[i] == t[j] ) { ++i; ++j; }
else
{
if( j == 0 ) /*当s[i] != t[0]时直接将i移至下一位进行比较*/
++i;
else
j = next[j];
}
}
if( j >= tlen )
return i - tlen;
else
return 0;
}