欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

每日算法练习——模式匹配KMP算法(看毛片算法?)

程序员文章站 2022-05-02 17:36:22
...

知识补充:

在计算机科学中,Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串S内查找一个词W的出现位置。此算法通过运用对这个词在不匹配时本身就包含足够的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符。

from wiki :https://zh.wikipedia.org/wiki/%E5%85%8B%E5%8A%AA%E6%96%AF-%E8%8E%AB%E9%87%8C%E6%96%AF-%E6%99%AE%E6%8B%89%E7%89%B9%E7%AE%97%E6%B3%95

代码实现C++

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void cal_next(char *str, int *next, int len)
{
    next[0] = -1;//next[0]初始化为-1,-1表示不存在相同的最大前缀和最大后缀
    int k = -1;//k初始化为-1
    for (int q = 1; q <= len-1; q++)
    {
        while (k > -1 && str[k + 1] != str[q])//如果下一个不同,那么k就变成next[k],注意next[k]是小于k的,无论k取任何值。
        {
            k = next[k];//往前回溯
        }
        if (str[k + 1] == str[q])//如果相同,k++
        {
            k = k + 1;
        }
        next[q] = k;//这个是把算的k的值(就是相同的最大前缀和最大后缀长)赋给next[q]
    }
}

int KMP(char *str, int slen, char *ptr, int plen)
{
    int *next = new int[plen];
    cal_next(ptr, next, plen);//计算next数组
    int k = -1;
    for (int i = 0; i < slen; i++)
    {
        while (k >-1&& ptr[k + 1] != str[i])//ptr和str不匹配,且k>-1(表示ptr和str有部分匹配)
            k = next[k];//往前回溯
        if (ptr[k + 1] == str[i])
            k = k + 1;
        if (k == plen-1)//说明k移动到ptr的最末端
        {
            //cout << "在位置" << i-plen+1<< endl;
            //k = -1;//重新初始化,寻找下一个
            //i = i - plen + 1;//i定位到该位置,外层for循环i++可以继续找下一个(这里默认存在两个匹配字符串可以部分重叠),感谢评论中同学指出错误。
            return i-plen+1;//返回相应的位置
        }
    }
    return -1;
}

int main()
{
    char str[255];
    char ptr[255];
    int sl,pl;  //str 和 ptr 的长度

    printf("请输入主串s和子串t,用空格分隔:\n");
    scanf("%s %s",str,ptr)

    sl=strlen(str);
    pl=strlen(ptr);
    printf("主串长度:%d 模式子串长度:%d\n",sl,pl);

    int pos = KMP(str, sl, ptr, pl);
    printf("子串在主串中的位置:%d\n",pos);

    return 0;
}

第二版修改

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// 修改成功

void get_next( char *T, int *next,int Tlen)
{
    int j = -1;
    int i;
    next[0] = -1;

    for(i=1 ; i<Tlen ; i++)
    {
        while(j>-1 && T[j+1]!=T[i])
        {
            j = next[j]; //回溯j
        }
        if(T[j+1] == T[i])
        {
            j++;
        }
        next[i] = j;
    }

    // 因为前缀是固定的,后缀是相对的。
}

// 测试get_next()函数的功能
// void main()
// {
//  char str[255] = "ababaaaba";
//  int next[255];
//  int i = 1;
//  str[0] = 1;
//  get_next(str, next);

//  for( i=1; i<10;i++  )
//  {
//      printf("%d\n",next[i] );
//  }
//  return 0;
// }

// 返回子串T在主串S中的位置,若不存在则返回-1
int KMP(char *S, int Slen, char *T, int Tlen)
{
    int i = -1;
    int j = -1;
    int next[255];

    get_next(T, next, Tlen);

    while(i<=Slen && j<=Tlen)
    {
        if( -1 == j || S[i] == T[j] )
        {
            i++;j++;
        }
        else
        {
            j = next[j];    // 回溯 j
        }
    }

    if( j >= Tlen )
    {
        return i-Tlen+1;    // 成功匹配返回位置
    }
    else
    {
        return -1;  //匹配失败,返回0
    }

}

int main(int argc, char const *argv[])
{
    char s[255],t[255];
    int pos;
    int slen, tlen;

    printf("请输入主串s和子串t,用空格分隔:\n");
    scanf("%s %s",s,t);
    slen = strlen(s);
    tlen = strlen(t);

    pos = KMP( s ,slen, t, tlen);
    printf("子串t在主串s中的位置为:%d\n",pos);



    return 0;
}
相关标签: 算法 KMP