字符串匹配kmp算法实现

B站灯笼大神的讲解视频:
av11866460 av16828557

1、把要匹配的字符串拆解成若干个子字符串
2、对于每一个子字符串(除了要匹配的字符串其本身),找出其中最长公共前后缀长度。
3、前缀表第一位补-1,其余位,顺位补最长公共前后缀长度的值。

首先是获取前缀表:

#include
#include
#include

void prefix_table(char *pattern, int *prefix, int n)
{
	//首先默认的将前缀表第一个空格写 0
    prefix[0] = 0;
    //i 和 len 用来作为比较字符时候的移动下标 len从下标0处开始
    int len = 0;
    //因为前缀表第一个空格已经是0 所以i从下标为1开始
    int i = 1;
    // n是整个pattern串的长度
    while( i < n )
    {
    	//当这两个字符相同时
        if( pattern[len] == pattern[i] )
        {
        	//len自增,以待进行下一次比对
            len++;
            //这时候由于字符相同,最长公共前缀增加了1,也就是len自增的结果
            prefix[i] = len;
            //i自增,以待下一次比对
            i++;
        }
        //如果不相同的话
        else
        {
        	// len>0是为了防止内存越界
            if( len > 0 )
            {
            	// 那么len 就等于prefix[len]值,斜着指向
                len = prefix[len-1];
            }
            else
            {
            //如果len==0或者len<0就让串回到最开始的地方开始比较
            //也就是此处的前缀表值为0
                prefix[i] = 0;
                i++;
            }
        }
    }
}
// 这里main函数是测试一下前缀表
int main()
{
    char pattern[] = "ABABCABAA";

    int n = 9;
    int *prefix = malloc(sizeof(int)*n);
    prefix_table(pattern, prefix, n);
    for(int i=0; i<n; i++)
    {
        printf("%d\n",prefix[i]);
    }

    return 0;
}

下面是移动前缀表,把前缀表移动成由-1为开头的一个数组


void move_table(int *prefix, int n)
{
    for(int i=n-1; i>0; i--)
    {
        prefix[i] = prefix[i-1];
    }
    prefix[0] = -1;
}

然后是kmp匹配的算法,完整代码:

#include
#include
#include

void prefix_table(char *pattern, int *prefix, int n)
{
    prefix[0] = 0;
    int len = 0;
    int i = 1;
    while( i < n )
    {
        if( pattern[len] == pattern[i] )
        {
            len++;
            prefix[i] = len;
            i++;
        }
        else
        {
            if( len > 0 )
            {
                len = prefix[len-1];
            }
            else
            {
                prefix[i] = 0;
                i++;
            }
        }
    }
}

void move_table(int *prefix, int n)
{
    for(int i=n-1; i>0; i--)
    {
        prefix[i] = prefix[i-1];
    }
    prefix[0] = -1;
}

void kmp_search(char *text, char *pattern)
{
	//text是长的那个模板字符串,pattern是短的待匹配的字符串
	//先获取pattern 和 text的长度
    int n = strlen(pattern);
    int m = strlen(text);
    //初始化prefix表
    int *prefix = malloc(sizeof(int)*n);
    //给prefix表填入值
    prefix_table(pattern, prefix, n);
    //移动表,使前缀表首位为-1
    move_table(prefix, n);
	//i来标识 text串,j来表示pattern串
    int i = 0;
    int j = 0;
    while( i < m )
    {
    	// 这个是恰好匹配时的状态,当满足的时候就是匹配成功的时候
        if( j == n-1 && text[i] == pattern[j] )
        {
        	// i-j的差就是匹配走过的字符个数
            printf("Fount pattern at %d\n", i-j);
            j = prefix[j];
        }
        // 字符匹配成功,就i++ j++继续匹配下一个
        if( text[i] == pattern[j] )
        {
            i++;
            j++;
        }
        // 没匹配成功呢 就更新一下j的值,
        //prefix表的作用就是定位,不用一个一个的移动,
        //定位的这个位置呢,是后续移动最少步就能继续匹配 的位置
        //所以kmp 算法最重要的是 prefix 表,有了这个表可以更快的知道
        //当这一次匹配失败时,下一次从哪里开始匹配是最快的。
        else
        {
        	//更新值
            j = prefix[j];
            if( j == -1 )
            {
                i++;
                j++;
            }
        }
    }
}

int main()
{
    char pattern[] = "ABABCABAA";
    /*
    int n = 9;
    int *prefix = malloc(sizeof(int)*n);
    prefix_table(pattern, prefix, n);
    for(int i=0; i
    char text[] = "ABAAABDAABABCABAAABCADD";
    kmp_search(text, pattern);
    return 0;
}

你可能感兴趣的:(c)