zoukankan      html  css  js  c++  java
  • KMP

    KMP算法:

    1、violentMatch()

    • 如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;
    • 如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。
    int violentMatch(string s,string p){
        int lens = s.length();
        int lenp = p.length();
        
        int i = 0,j = 0;
        while(i<lens&&j<lenp){
            if(s[i]==p[j]){
                i++,j++;
            }else{
                i= i-j+1,j=0;
            }
        }
        if(j==lenp) return i-j;
        else return -1;
    }
    View Code

         

    S[10]与P[6]不匹配,所以文本串回溯到S[5],模式串回溯到P[0],从而让S[5]跟P[0]匹配

    而S[5]肯定跟P[0]失配。为什么呢?因为在之前第4步匹配中,我们已经得知S[5] = P[1] = B,而P[0] = A,即P[1] != P[0],故S[5]必定不等于P[0],所以回溯过去必然会导致失配。

    它利用之前已经部分匹配这个有效信息,保持i 不回溯,通过修改j 的位置,让模式串尽量地移动到有效的位置。即下面的KMP算法

    2、KMP:

    next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。

    通过代码递推计算next 数组

        接下来,咱们来写代码求下next 数组。

        基于之前的理解,可知计算next 数组的方法可以采用递推:

    • 1. 如果对于值k,已有p0 p1, ..., pk-1 = pj-k pj-k+1, ..., pj-1,相当于next[j] = k。
      • 此意味着什么呢?究其本质,next[j] = k 代表p[j] 之前的模式串子串中,有长度为k 的相同前缀和后缀。有了这个next 数组,在KMP匹配中,当模式串中j 处的字符失配时,下一步用next[j]处的字符继续跟文本串匹配,相当于模式串向右移动j - next[j] 位。

    举个例子,如下图,根据模式串“ABCDABD”的next 数组可知失配位置的字符D对应的next 值为2,代表字符D前有长度为2的相同前缀和后缀(这个相同的前缀后缀即为“AB”),失配后,模式串需要向右移动j - next [j] = 6 - 2 =4位。

    向右移动4位后,模式串中的字符C继续跟文本串匹配。

    • 2. 下面的问题是:已知next [0, ..., j],如何求出next [j + 1]呢?

        对于P的前j+1个序列字符:

    • 若p[k] == p[j],则next[j + 1 ] = next [j] + 1 = k + 1;
    • 若p[k ] ≠ p[j],如果此时p[ next[k] ] == p[j ],则next[ j + 1 ] =  next[k] + 1,否则继续递归前缀索引k = next[k],而后重复此过程。 相当于在字符p[j+1]之前不存在长度为k+1的前缀"p0 p1, …, pk-1 pk"跟后缀“pj-k pj-k+1, …, pj-1 pj"相等,那么是否可能存在另一个值t+1 < k+1,使得长度更小的前缀 “p0 p1, …, pt-1 pt” 等于长度更小的后缀 “pj-t pj-t+1, …, pj-1 pj” 呢?如果存在,那么这个t+1 便是next[ j+1]的值,此相当于利用已经求得的next 数组(next [0, ..., k, ..., j])进行P串前缀跟P串后缀的匹配。
    • PS:卡在这里:

       

    • 若p[k] == p[j],则next[j + 1 ] = next [j] + 1 = k + 1;
    • 若p[k ] ≠ p[j],如果此时p[ next[k] ] == p[j ],则next[ j + 1 ] =  next[k] + 1,否则继续递归前缀索引k = next[k],而后重复此过程。 相当于在字符p[j+1]之前不存在长度为k+1的前缀"p0 p1, …, pk-1 pk"跟后缀“pj-k pj-k+1, …, pj-1 pj"相等,那么是否可能存在另一个值t+1 < k+1,使得长度更小的前缀 “p0 p1, …, pt-1 pt” 等于长度更小的后缀 “pj-t pj-t+1, …, pj-1 pj” 呢?如果存在,那么这个t+1 便是next[ j+1]的值,此相当于利用已经求得的next 数组(next [0, ..., k, ..., j])进行P串前缀跟P串后缀的匹配。
    • void getNext(string p){
          int lenp = p.length();
          next[0] = -1;
          int k = -1,j=0;
          while(j<lenp-1){
              if(k==-1||p[k]==p[j]){
                  j++;
                  k++;
                  next[j] = k;
              }else{
                  k = next[k];
              }
          }
          
      }
      View Code

      总体代码:

    • #include<iostream>
      using namespace std;
      const int maxn = 1e5+5;
      int next[maxn];
      void getNext(string p){
          int lenp = p.length();
          next[0] = -1;
          int k = -1,j=0;
          while(j<lenp-1){
              if(k==-1||p[k]==p[j]){
                  j++;
                  k++;
                  next[j] = k;
              }else{
                  k = next[k];
              }
          }
          
      }
      int violentMatch(string s,string p){
          int lens = s.length();
          int lenp = p.length();
          
          int i = 0,j = 0;
          while(i<lens&&j<lenp){
              if(s[i]==p[j]){
                  i++,j++;
              }else{
                  i= i-j+1,j=0;
              }
          }
          if(j==lenp) return i-j;
          else return -1;
      }
      int kmp(string s,string p){
          int lens = s.length();
          int lenp = p.length();
          
          int i=0,j=0;
          while(i<lens && j<lenp){
              if(j==-1||s[i]==p[j]){
                  i++,j++;
              }else{
                  j = next[j];
              }
              cout<<"KMP"<<endl;
          }
          if(j==lenp) return i-j;
          else return -1;
      }
      int main()
      {
          int ans = 0;
          string s = "BBCABCDABABCDABCDABDE";
          string p = "ABCDABD";
          
          ans = violentMatch(s,p); 
          cout<<ans<<endl;
          
          getNext(p);
          for(int i=0;i<p.length();i++){
              cout<<next[i]<<" ";
          }
          //ans = kmp(s,p); 
          cout<<ans<<endl;
          
          return 0;
      } 
      View Code
  • 相关阅读:
    一天快速入门Python语法基础之类与继承
    一天快速入门Python语法基础之函数
    一天快速入门Python语法基础之用户输入和if while语句
    一天快速入门Python语法基础之字典
    一天快速入门Python语法基础之操作列表
    应用Maven管理SringMvc+MyBaties登录功能的实现
    在eclipse中使用Maven以及HelloWorld的实现
    C++和Java实现基本的选择排序和常见错误
    Maven的安装和配置以及HelloWorld的实现
    POJ--3311--Hie with the Pie(暴力枚举+floyd)/(状态压缩DP+floyd)
  • 原文地址:https://www.cnblogs.com/Lemon1234/p/11656770.html
Copyright © 2011-2022 走看看