kmp及其運用

一、算法介紹

D.E. Knuth、 JH.Morris和 R. Pratt(其中 Knuth和Prat共同研究, Morris獨立研究)發表一個模式匹配算法,可以大大避免重複遍歷的情況,我們把它稱之爲克努特一莫里斯一普拉特算法,簡稱KMP算法

兩個我覺得講的可以的視頻

  1. 算法介紹:https://www.bilibili.com/video/BV1Ys411d7yh
  2. 介紹&&代碼復現:https://www.bilibili.com/video/BV1hW411a7ys

KMP算法核心部分就是獲取匹配數組(有的叫next,有的叫pattern數組):

image-20200619114623242

代碼實現時需要注意有的用-1表示,有的用0表示開始

二、相關題目

1. LeetCode 28. 實現 strStr()

 實現 strStr() 函數。

 給定一個 haystack 字符串和一個 needle 字符串,在 haystack 字符串中找出 needle 字符串出現的第一個位置 (從0開始)。如果不存在,則返回 -1。

示例 1:

輸入: haystack = "hello", needle = "ll"
輸出: 2

示例 2:

輸入: haystack = "aaaaa", needle = "bba"
輸出: -1

說明:

 當 needle 是空字符串時,我們應當返回什麼值呢?這是一個在面試中很好的問題。

 對於本題而言,當 needle 是空字符串時我們應當返回 0 。這與C語言的 strstr() 以及 Java的 indexOf() 定義相符。

C代碼實現:

int* get_next(char* needle){
    int lenOfneedle=strlen(needle);
    int* next=(int *) malloc(sizeof(int)*lenOfneedle);
    next[0] = -1;
    int j = 0;
    int k = -1;
    while(j<lenOfneedle - 1){
        if(k == -1 || needle[k] == needle[j]){
            j++;
            k++;
            next[j] = k;
        }else{
            k = next[k];
        }
    }
    return next;
}


int strStr(char* haystack, char* needle){
    int lenOfhaystack=strlen(haystack),lenOfneedle=strlen(needle);
    if(lenOfneedle==0)
        return 0;
    int* next = get_next(needle);
    int i=0;
    int j=0;
    while(i < lenOfhaystack && (j <lenOfneedle)){
        if(j == -1 || haystack[i] == needle[j]){
            i++;
            j++;
        }else{
            j = next[j];
        }

    }
    if(j == lenOfneedle){
        return i-j;
    }else{
        return -1;
    }
}

C++代碼實現

class Solution {
public:
    vector<int> getNext(string pattern){
        int len=pattern.size();
        vector<int> next(len);
        int i=-1,j=0;
        next[0]=-1;
        while(j<len-1){
            if(i==-1||pattern[i]==pattern[j]){
                i++;
                j++;
                next[j]=i;
            }else{
                i=next[i];
            }
        }
        return next;
    }
    int strStr(string haystack, string needle) {
        if(needle.size()==0)
            return 0;
        vector<int> next=getNext(needle);
        int i=0,j=0;
        int len1=haystack.size(),len2=needle.size();
        while(i<len1&&j<len2){
            if(j==-1||haystack[i]==needle[j]){
                i++;
                j++;
            }else{
                j=next[j];
            }
        }
        if(j==needle.size())
            return i-j;  //開始的位置
        return -1;
    }
};

 需要注意的是C++中main函數while循環裏的“i<len1&&j<len2”,如果直接用needle.size()會出問題,因爲默認返回值是無符號整數不是int,和-1直接比較結果是不對的,這裏找了半天。

2. 查找字符串最長公共子串

 請編碼實現一個命令行工具,找出指定的2個字符串的最長公共子串。

輸入描述:
命令行工具接收兩個字符串參數。輸入字符串的合法字符集爲[a-zA-Z0-9],大小寫敏感,無需考慮異常輸入場景。
輸出描述:
所找到的公共子串;如果存在多個等長的公共子串,則請按字母序排序,依次打印出所有公共子串,每行一個。

示例1

輸入

1234567 12893457

輸出

345

 這裏可以用枚舉(順序表)+KMP,也可以用動態規劃,這裏先貼dp的代碼(評論區)

#include<iostream>
#include<string>
#include<math.h>
#include<algorithm>
using namespace std;
 
void findLCStr(string A, int n, string B, int m) {
    int c[n+1][m+1];
    pair<int, string> p[min(n,m)];
    int i,j,k,res=0,res_end=0,cnt=0;//res 最長公共子串長度,res_end最長公共子串末尾序號
    for(i=0;i<=n;i++) c[i][0]=0;
    for(j=1;j<=m;j++) c[0][j]=0;
 
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            if(A[i-1]==B[j-1]){
                c[i][j] = c[i-1][j-1] + 1;
                if(res<=c[i][j]){
                    res = c[i][j];
                    res_end = i;
                    string t="";
                    p[cnt].first=res;
                    for(k=res_end-1-res+1;k<=res_end-1;++k)
                        t+=A[k];
                    p[cnt++].second=t;
                }
                //res = max(res, c[i][j]);
            }
            else c[i][j] = 0;    //與LCS的區別在這裏
        }
    }
    sort(p,p+cnt);
    for(i=0;i<cnt;i++){
        if(p[i].first==res)
            cout<<p[i].second<<"\n";
    }
}
int main(){
    string A,B;
    cin>>A>>B;
    findLCStr(A,A.length(),B,B.length());
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章