zoukankan      html  css  js  c++  java
  • KMP算法详解

    前置:


      尝试去思考这样一个问题:给定字符串S和P,询问在字符串S中,字符串P出现了几次?

       设S = "aabcdedf", P = "abc"; 

       我们先从最暴力的方法入手,不难想到去针对S的每一位去和P暴力匹配。

       当 $i = 0$ 时, 字符串匹配如下图:

       

       $s[0] == p[0]$ 则继续向后匹配,发现 $s[1] != p[1] $,则执行 $i++$ 进行下一次匹配。

       当 $i = 1$ 时,匹配如下图

        

       $s[0] == p[0]$ 继续向后匹配, 直到 $s[i + strlen(p) - 1] == p[strlen(p) - 1]$ 证明匹配成功。

       令 $ans++$,并且 $i++$ 进行下一次匹配。

       按上述方法匹配直到字符串S被遍历一遍,输出 $ans$ 即可。

       代码:

     1 int fun(char *s, char *p){
     2     int ans = 0;
     3     for (int i = 0; i < strlen(s); ++i){
     4         int j = 0; 
     5         for (; j < strlen(p); ++j){
     6             if (s[i + j] == p[j])
     7                 continue;
     8             else
     9                 break;
    10         }
    11         if (j == strlen(p))
    12             ++ans;
    13     }
    14     return ans;
    15 }
    View Code

       设字符串S的长度为 $n$ , P的长度为 $m$ ,不难计算出该暴力算法的复杂度为 $O(nm)$,在数据量较大的时候必定超时。

    KMP算法:


      KMP算法则是对上述过程中的一个优化,使得在每次匹配的过程中,当遇到失配的情况时,可以通过之前已经匹配的信息优化匹配过程,而不用每次都从字符串P的起始位置重新匹配。

      这个优化的过程就是通过 $next$ 数组实现的。 我们将字符串S称为文本串,将字符串P成为模式串

      KMP算法分为两个阶段:1、求模式串的 $next$ 数组。 2、结合 $next$ 数组进行匹配。

      $next$ 数组:$next[i]$ 的含义为:模式串第 $i$ 位与文本串适配时,下一次匹配应该从 模式串第 $next[i]$ 个位置开始匹配。

      图例:

      

      上图匹配过程中在 $p = 6$ 时发生失配,结合 $next$ 数组此时我们下一次匹配的起始位置应是:

      

      因为右移四位后,模式串中又会有一个“AB”与文本串匹配。从而不用 $i$ 移动。

      不难得出结论: $next[i]$ 为 $i$ 之前的模式串的最长公共前后缀的长度

      之后去求 $next$ 数组的过程其实是模拟串自我匹配的过程

      

    int nxt[1005];
    
    void get_next(char *s){
        int lens = strlen(s);
        int i = 0, j = -1;
        nxt[0] = -1;//默认nxt[0] = -1
        while (i < lens){
            //如果j = -1 或者当前两位相同,则最长公共前后缀可往后扩展。 
            if (j == -1 || s[i] == s[j]){
                ++i;
                ++j;
                nxt[i] = j;//记录当前位置最长公共前后缀的长度。 
            }
            //失配则i不变, j需移动到nxt[j]的位置继续匹配 
            else{
                j = nxt[j];
                
            } 
        } 
    }
    View Code

      在求得 $next$ 数组之后,我们便可以根据 $next$ 数组进行匹配。

    int kmp(char *s, char *p){
        //s为文本串, p为模式串
        int lens = strlen(s), lenp = strlen(p);
        int ans = 0;
        int i = 0, j = 0;
        while (i < lens){
            //若j = -1或者匹配成功则继续匹配 
            if (j == -1 || s[i] == p[j]){
                ++i;
                ++j;
            }
            //失配则i不变, j移动到nxt[j]位置继续匹配 
            else{
                j = nxt[j];
            } 
            //匹配成功 
            if (j == lenp)
                ++ans; 
        } 
        return ans; 
    } 
    View Code

      以上便是KMP算法的大体内容,复杂度为 $O(n + m)$。

      

      

  • 相关阅读:
    hdu 4521 小明系列问题——小明序列(线段树 or DP)
    hdu 1115 Lifting the Stone
    hdu 5476 Explore Track of Point(2015上海网络赛)
    Codeforces 527C Glass Carving
    hdu 4414 Finding crosses
    LA 5135 Mining Your Own Business
    uva 11324 The Largest Clique
    hdu 4288 Coder
    PowerShell随笔3 ---别名
    PowerShell随笔2---初始命令
  • 原文地址:https://www.cnblogs.com/zssst/p/11806898.html
Copyright © 2011-2022 走看看