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
原文地址:https://www.cnblogs.com/Lemon1234/p/11656770.html