zoukankan      html  css  js  c++  java
  • KMP初步

    本人刚学KMP,望大佬指出说法不合适的地方......

     

    KMP这个算法主要可以用来解决求字符串中子串的位置(当然不止这一点),绝对不是字面上的那个什么意思......它是个算法!算法!全称Knuth-Morris-Pratt!

    对了,有人会问:既然求子串,那为什么不用c++库里自带的 strstr(str1,str2) 函数呢? 告诉你,那东西是暴力!用暴力求的!大数据过不了!

    所以我们才要KMP......

    例题:

    题目描述

    给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置。

    输入输出格式

    输入格式:

    第一行为一个字符串,即为s1

    第二行为一个字符串,即为s2

     

    输出格式:

    若干行,每行包含一个整数,表示s2在s1中出现的位置

    输入输出样例

    输入样例

    ABABABC
    ABA

    输出样例

    0
    2

     

    先令 s1 长度为 n , s2 长度为 m 。KMP的算法就是先用 O(m) 的时间对 s2 进行预处理,再用 O(n) 的时间完成匹配。

    它和朴素的算法的区别就是:若 s1[i] != s2[j] 那么朴素算法会从 s2 的头开始匹配;而KMP会从已有的最长的 s2 的部分开始匹配。

    举个例子:s2 = abbaaba,当 s1[i] 和 s2[6] ('a') 比较时,若 s1[i] != s2[5] 朴素的算法就是回到 s2 的头重新比较,而 KMP 可以利用 s2 本身的特性判断出 再和 s2[3] ('b') 比较是有可能匹配的。画一个图,可能会直观些。以下是一个状态转移图,每一个圈代表一个状态,箭头下的字母代表该状态读到这个字母而转变成下一个状态。

     

    从这个图就可以看出,若状态6的下一个不是 'a' 的话,就会到状态2,。换句话说,就是到状态6的这个串应该回到和它的最长后缀相等的 s2 的前缀,也就是这里的从状态4开始的 "ab",和从状态0开始的 "ab"。

    所以对于每一个状态,要么是匹配对了进入下一个状态,要么不对(失配)返回以前的某个状态。对于失配状态,我们只需用一个一位数组 f[m] 来储存,则 f[i] 表示状态 i 失配时应转移到的状态,特别注意 f[0] = 0。

    有了这个失配函数后,KMP的代码就可以写出来了:

     1 void kmp(char t[], char p[], int f[])
     2 {
     3     int n = strlen(t), m = strlen(p);
     4     getfail(p, f, m);    //构造失配函数 
     5     int j = 0;            //代表当前状态编号,初始为0 
     6     for(int i = 0; i < n; ++i)
     7     {
     8         while(j && t[i] != p[j]) j = f[j];    //按失配边走,直到可以匹配 
     9         if(p[j] == t[i]) j++;    //因为可能回到头,所以还要判断一下 
    10         if(j == m) printf("%d
    ", i - m + 2);    //匹配成功,输出匹配点位置 
    11     }
    12 }

    接下来就是就要构造失配函数,即状态转移图,这是构造KMP算法的关键。思想是”用自己匹配自己“,根据 f[0], f[1] ...... 递推 f[i]。

    还是放图更直观些

    假如到状态<2> 的失配函数是<1>,那么必定有<1> == <2>,此时再进入下一个状态 i,若<1> 的下一个状态 j == i,那么 i 失配时就一定转移到状态 j ;若 i != j,再找 j 失配时应转移到的状态 k ,在比较 i 和 k。相等时,i 失配就转移到 k,否则继续找,直到转移到0,此时 i 失配时就回到初始状态了。

    代码

     1 void getfail(char p[], int f[], int m)
     2 {
     3     f[0] = 0; f[1] = 0;        //递推边界初值 
     4     for(int i = 1; i < m; ++i)
     5     {
     6         int j = f[i];
     7         while(j && p[j] != p[i]) j = f[j];
     8         f[i + 1] = p[j] == p[i] ? j + 1: 0;
     9          //若没回到初始状态,那么 i + 1状态就转移到 j + 1 
    10     }
    11     return;
    12 }

    这就是KMP的板子了,要想真正学好KMP,一定要将这个失配函数怎样构造的理解透彻,最好别死记硬背板子。

  • 相关阅读:
    产生6位的随机码
    用户输入注册的小程序
    进度条的小程序
    文件的相关操作---读写与修改
    字符串分割(C++) 转载
    C++ Stream与编码转换
    wxWidgets界面设计工具DialogBlocks(转载)
    WIN32窗口模板
    WTL对话框应用程序响应键盘消息
    常用网站
  • 原文地址:https://www.cnblogs.com/mrclr/p/8362586.html
Copyright © 2011-2022 走看看