KMP算法

KMP算法

  • 1.KMP算法介绍及其理论
      • 什么是KMP算法
  • 2.KMP算法的理论
      • 2.1 前缀表
      • 2.2如何求next数组
      • 2.3KMP算法的代码
  • 3.KMP算法的相关题目

1.KMP算法介绍及其理论

什么是KMP算法

KMP算法是解决字符串的匹配问题的算法,是用来判断一个字符串是不是另一个字符串的子串的一种算法。设两个字符串的长度分别为m,n。KMP算法的时间复杂度为O(m+n)。

2.KMP算法的理论

我们先看下面这两个字符串
KMP算法_第1张图片
下面我们来进行匹配
KMP算法_第2张图片KMP算法_第3张图片

发现匹配到这个位置的时候,匹配不上了,如果按照暴力的方法,我们重头开始,把指针 j 回退到开头,但是我们可以发现其实是可以不从头开始匹配的,看下面这个图:
KMP算法_第4张图片
我们发现在在已经匹配好的aabaa中,后缀aa已经在之前已经匹配成功了,又因为前缀中也存在aa,那么我们的 j 指针就可以直接回退到前缀aa之后,再进行匹配,我们可以把 j 指针回退到,前缀aa后一个位置进行下一步匹配。

KMP算法_第5张图片
下面继续重新开始匹配
KMP算法_第6张图片
最后匹配完成。我们发现如果出现了匹配不成功,我们 j 因该回退到和后缀相同的最长前缀的后面,我们是可以处理出每个位置的最长相等前后缀的,我们通过一个数组把它存起来,这个数组就是前缀表,下面来介绍一下前缀表。

2.1 前缀表

首先我们来介绍一下字符串的前缀和后缀的概念。

前缀:字符串从左开始的任意子串(或者说是字符串的任意首部)

真前缀(又称前缀真子串):是指不包含本身的前缀。

后缀定义:字符串从右开始的任意子串(或者说是字符串的任意尾部)

真后缀(又称后缀真子串):是指不包含本身的后缀。

我们以上面的字符串aabaaf为例

子串 最长相等前后缀
a 0
aa 1
aab 0
aaba 1
aabaa 2
aabaaf 0

这个就是我们的前缀表,一般用next数组储存

注意:这里的最长相等前后缀不能是字符串本身

2.2如何求next数组

下面看两种情况:

前后缀相等情况
KMP算法_第7张图片

前后缀不等情况
KMP算法_第8张图片如果不明白回退的过程我们还可以这么看,把他看成两个相同字符串。
KMP算法_第9张图片j回退到next[j-1]位置时

KMP算法_第10张图片继续回退
KMP算法_第11张图片下面是最后求next数组的代码

//求next数组
//初始化
next[0]=0;
for(int i=1,j=0;i<len;i++)
{
     while(s[i]!=s[j]&&j>0) j=next[j-1];
     if(s[i]==s[j])j++;
     next[i]=j;
}

2.3KMP算法的代码

上面求出了next数组,我们就可以写出kmp算法的匹配了。

int Search(char* str1,char* str2)
{
    int len = strlen(str1);
    for (int i = 0,j = 0; i <len ; ++i)
     {
       // 如果不相等回退
        while (j>0 && str1[i] != str2[j])
            j = next[j-1];
        //相等继续向前匹配
        if (str1[i] == str2[j])
            j++;
           
         //返回下标
        if (j == strlen(str2))
            return i-j+1;
    }
    return -1;
}

3.KMP算法的相关题目

重复的子字符串
这个题就是用kmp来求循环节了,如果一个字符串可以由重复子串构成,那么一个循环节就是len-next[len-1],这里我就不证明了。下面是代码

bool repeatedSubstringPattern(string s)
{
      //求next数组
      int next[10010];
      next[0]=0;
      int len=s.size();
      
    for(int i=1,j=0;i<len;i++)
    {
        while(s[i]!=s[j]&&j>0) j=next[j-1];
        if(s[i]==s[j])j++;
        next[i]=j;
    }
    //判断是否有循环节
    int t=s.size()/(s.size()-next[len-1]);
    
    if(s.size()%(s.size()-next[len-1])==0&&t>1) return true;
    else return false;
}

你可能感兴趣的:(算法,c++)