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)$。

      

      

  • 相关阅读:
    4个常用的HTTP安全头部
    Content Security Policy 入门教程
    前端安全配置之Content-Security-Policy(csp)
    关于setConnectTimeout和setReadTimeout的问题
    Maven库下载很慢解决办法,利用中央仓库
    ibatis Order By注入问题
    Web系统常见安全漏洞及解决方案-SQL盲注
    玩得一手好注入之order by排序篇
    python接口自动化29-requests-html支持JavaScript渲染页面
    python接口自动化28-requests-html爬虫框架
  • 原文地址:https://www.cnblogs.com/zssst/p/11806898.html
Copyright © 2011-2022 走看看