【算法】KMP算法超详好懂理解(附全程序模拟过程)

来源:互联网 发布:java的compareto 编辑:程序博客网 时间:2024/06/03 19:36

KMP算法详解

当前稿件版本1.0

  • KMP算法详解
    • 前言
    • 正文
      • KMP是什么
      • KMP大体是怎么运作的之规定篇
      • KMP大体是怎么运作的之模拟主程序solve篇
      • KMP大体是怎么运作的之模拟部分匹配值PMT_find篇
    • 代码

前言

      今天为了NOIP提高组的模拟准备,早上起来——默写KMP吧!好,提交:TLE;改改改:TLE;我不信!:TLE;58%%RS@^$:WA
     终于在一天的努力后,把KMP给A了。
痛心疾首,决心写一个KMP的详解,不要让其他人掉入这个坑里了

正文

KMP是什么

来源在哪里不用管!只用知道它是一个高效的字符串匹配算法就行了。
AC自动机就是在Trie树上做一个KMP罢了。
故学好KMP还是蛮重要地。

KMP大体是怎么运作的之规定篇

嘿嘿嘿,开始模拟吧!
等等!直接开始模拟会有一些奇怪的问题,不利于理解,毕竟这个博客打的是超详好懂理解的标题,自然要先做出一些规定:
    1. father son均为从0开始的string类型,father son分别为父串和子串
    2. PMT为部分匹配值,为了和son保持一致,也是从0开始。
    3. 变量i为father正在对比的字符下标【模拟中以红色标出】
    4. 变量compared为son正在对比的字符下标【模拟中以黄色标出】
    5. compared也代表了有几个字符匹配成功
好了开始模拟吧

KMP大体是怎么运作的之模拟主程序solve篇

以6do8_8__8q找出8__8q为例
这个是最原始的表格:
origin
add为下标的号码,father、son同规定所示
有一个数组,名为PMT(Partial Match Table),部分匹配值的英文全称的简写。通过PMT_find函数求出其值(这里省略,下文会提及)
PMTfinded
开始对比,初始化compared=0;for(i=0;i<father.size();++i)
inited
此时两个字符不等,compared为0,所以i++,compared保持不动
i+c0
同理直至i=3时
i=3
因为son[compared]==father[i],所以compared++,i++
allp
但是当i=5,compared=2时,又一次不相等
52233
此时按照暴力来说,自然是compared=0,i++但是KMP不一样
它会搞一个大新闻,将compared=PMT[compared-1]等等,是0?额…是这个样例的问题,不用理!(注释:这一段的操作用文字叙述就是:用已匹配的字符数-其对应的部分匹配值)
233
例如在father="BBBBA",son="BBA"时,i=2,compared=2时会这样子:
after233
compare=PMT[compared-1],即为1,因为循环的因素i依然++,于是就成为了这样子
2222233333
看出PMT可以减去一些枚举。
接下来就是各种compared++与i++了,最后compared==son.size(),结束循环
这里写图片描述
注意:此时son[compared]越界了,所以注意程序处理顺序

KMP大体是怎么运作的之模拟部分匹配值PMT_find篇

部分匹配值的官方解释是:”前缀”和”后缀”的最长的共有元素的长度。
“部分匹配”的实质是,有些字符串头部和尾部会有重复。比如,”ABCDAB”之中有两个”AB”,那么它的”部分匹配值”就是2(”AB”的长度)。搜索词(即i、compared移动)移动的时候,第一个”AB”向后移动4位(字符串长度-部分匹配值),就可以来到第二个”AB”的位置。
那么怎么求呢,就是根据上一位以及上一位的字串来进行判断求max(因为字串的部分匹配值总是比母串大,所以直接搜就可以了)
- 貌似还是有点难懂啊,就拿ABABAC说吧,假设正在第3个A上做判断,前面有了个ABA,如果这里(即以第三个A为最后一个字符)有一个ABA——岂不美哉?
- 假设正在C上做判断,前面有一个ABAB,如果这里有一个ABAC不好么?然而没有…好的,那么此时是不是搜第二个B的BA?不是的,ABABA的最长出现字串是AB(BA也可以,但是C后面是A,A的视角中是AB),因此AB==AC?不,这么一直搜下去,没有……那就是0呗!
不懂的朋友们,拿起纸和笔,来手动按照下面的源码来一发模拟吧!自己动手才好理解

代码

/*build 2017.7.22*/#include<iostream>#include<cstring>#include<cstdio>using namespace std;const int maxlenth=100001;bool flag=0;int want;int PMT[maxlenth];string father;string son;void PMT_find(){    int comparing;    PMT[0]=0;    for(int i=1;i<want;++i)    {        comparing=PMT[i-1];        while(son[comparing]!=son[i])        {            if(comparing==PMT[comparing]) break;            comparing=PMT[comparing];        }        if(son[comparing]==son[i]) ++comparing;        PMT[i]=comparing;    }}void solve(){    int compared=0;    for(int i=0;i<father.size();++i)    {        /*if(father[i]!=son[compared])        {            i=i+(compared-PMT[compared]);            compared=PMT[compared];        }        if(father[i]==son[compared]) ++compared;*/        //优化        while(compared and father[i]!=son[compared])        {            compared=PMT[compared-1];        }        if(father[i]==son[compared]) ++compared;        if(compared==want)        {            flag=1;            cout<<i+1-want+1<<endl;            compared=PMT[compared-1];        }    }}int main(){    ios::sync_with_stdio(false);    cin>>father>>son;    want=son.size();    PMT_find();    /*    for(int i=0;i<son.size();++i)    {        cout<<PMT[i]<<" ";    }    cout<<endl;    */    solve();    if(flag==0)    {        cout<<"no"<<endl;    }    return 0;}
原创粉丝点击