KMP算法的理解与认识

 

如果我们要找到一个子串在一个字符串第一次出现的位置,可以使用BF算法或者KMP。

其中,BF算法的原理就是将子串字符与主串字符挨个比较,一旦不同,就返回到首个相同的字符的下一个在进行比较,直到全部相同位置。

如:子串abac,主串ababacdac,其步骤如下:

ababacdac
a
ababacdac
ab
ababacdac
aba
ababacdac
 a
ababacdac
  a
ababacdac
  abac

但是,这里我们发现,在第4步时,不需要将子串的a同主串的b进行比较,因为他们一定不会相等。如果是很多位的子串势必会造成很多不要的浪费,有没有办法避免这种情况?

答案是肯定的,KMP这是因为BF存在这种缺陷而诞生的。

按照我们的逻辑,第4步应该是这样的:

ababacdac
  ab

而我们比较的是主串的第四位上的b和子串的第二位上的b。

这里我们引入一个概念,最长前缀后缀。如aba的最长前后缀为1,aabaa的最长前后缀为2,aabaab最长前后缀为3 。。。

如果要达到我们想要的效果,势必要记录子串的前缀在主串中的位置,这样才能快速匹配。

如本例中的子串abac,它的前缀有a,ab,aba。同时我们需要定义一个next数组,用来记录最长前后缀,每一个下标对应之前的最长前后缀。默认数组第一位为-1,如本例中的abac,对应的数组为 -1 0 0 1。

每次检查子串和主串对应字符是否相同:

如果相同,则都往后移一位继续比较,直到走到子串尽头

如果不同,则子串后移 j-next[j]位,即到达本例中的b,可以看到,子串此时的b之前的字符主串也有,即本例中的a。因为我们是将最长前后缀作为数组的值,next[j]则表示已经匹配到的位置的下一位,那么子串的前部分就意味着已经匹配了,这样就做到了不必回到原来的位置了!!!!

下面给出完整源码:

#include<bits/stdc++.h>
using namespace std;
int next[100100];
void get_next(string &str1)
{
    int i=0,j=-1;
    next[0]=-1;
    while(i<str1.length()){
        if(j==-1 || str1[i]==str1[j]){                //每次会回到起点,目的在于计算i之前的最长前后缀(必须理解!!)
            i++,j++;
            if(str1[i]!=str1[j] || i>j+1) next[i]=j;    //将前面的最长前后缀赋值给ext[i]
            else next[i]=next[j];                     //如果相邻两个字符相同,则直接赋值
        }
        else j=next[j];                               //多次执行后回到起点
    }
}
int index_KMP(string &str,string &str1,int &pos)
{
    int i=pos,j=0,m=str1.length();
    while(i<str.length()){
        if(j==-1 || str[i]==str1[j]){ i++; j++; }      //主子串后移一位
        else j=next[j];                                //子串后移到匹配位置
        if(m<=j) return i-j+1;
    }
    return -1;
}
int main(){
    string str,str1;
    getline(cin,str);   //主串
    getline(cin,str1);  //子串
    get_next(str1);
    int pos=0;      //表示开始查找的位置
    int index=index_KMP(str,str1,pos);
    cout<<index<<endl;
    return 0;
}

KMP的重点在于理解next数组,理解next数组在于每次到了一个新位置,j 会回到起点往后查找 i 的最长前后缀!理解了这一点KMP的核心思想也就掌握了!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值