KMP 算法——C

    昨天看了一下用于字符子串查找的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;

}

    

posted @ 2012-09-20 14:53  糙哥  阅读(480)  评论(0编辑  收藏  举报