日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

字符串匹配算法(三):KMP(KnuthMorrisPratt)算法

發(fā)布時(shí)間:2024/4/11 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符串匹配算法(三):KMP(KnuthMorrisPratt)算法 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • KMP
  • 原理
  • next數(shù)組的構(gòu)建
  • 代碼實(shí)現(xiàn)


KMP

一提到字符串匹配算法,想必大家腦海中想到的第一個(gè)必然就是KMP算法,KMP算法的全稱叫做KnuthMorrisPratt算法,與上一篇博客中介紹的BM算法一樣,它的核心也是通過(guò)滑動(dòng)來(lái)減少不必要的匹配


原理

與BM算法不同,KMP算法是從前往后進(jìn)行比較的,并且其將專注點(diǎn)放在已匹配的前綴上,為了方便理解,在這里我們還是使用BM算法中的好前綴和壞字符的概念。

例如下列字符串

還是老樣子,我們?cè)噲D在已匹配的前綴中找到某種規(guī)律,使得我們能夠一下進(jìn)行大量的滑動(dòng),我們此時(shí)就對(duì)好前綴進(jìn)行分析。

此時(shí)我們發(fā)現(xiàn),對(duì)于好前綴來(lái)說(shuō),其存在兩組完全相同的前綴和后綴,分別是aba和a。

此時(shí)我們就可以利用這個(gè)相同的后綴,直接將模式串對(duì)其到后綴的起始位置,為了保證偏移量最大,我們選擇其中最長(zhǎng)的那一組,如下圖

為了表述方便,我將這組前后綴子串分別稱為最長(zhǎng)可匹配前綴子串最長(zhǎng)可匹配前綴子串

KMP的整體思路就是在已匹配的前綴當(dāng)中尋找到最長(zhǎng)可匹配后綴子串和最長(zhǎng)可匹配前綴子串,在下一輪直接把兩者對(duì)齊,從而實(shí)現(xiàn)模式串的快速滑動(dòng)


next數(shù)組的構(gòu)建

為了避免每次都要去尋找這組最長(zhǎng)匹配的前后綴,我們將其緩存到一個(gè)數(shù)組中,等到使用的時(shí)候再去取,這個(gè)數(shù)組也就是我們經(jīng)常提到的next數(shù)組,而它的構(gòu)建也是KMP算法中最難理解的地方

數(shù)組的下標(biāo)是每個(gè)前綴結(jié)尾字符下標(biāo),數(shù)組的值是這個(gè)前綴的最長(zhǎng)可以匹配前綴子串的結(jié)尾字符下標(biāo)。

那么要如何構(gòu)建next數(shù)組呢?我們需要借助到動(dòng)態(tài)規(guī)劃和回溯的思想,在大部分算法書(shū)和博客中都將這塊描述的十分復(fù)雜,下面我就將其分解成多個(gè)問(wèn)題,來(lái)方便理解

next數(shù)組構(gòu)建時(shí)存在以下兩種情況,為了方便表述,我將后綴起點(diǎn)定為i,前綴起點(diǎn)定為j

  • 前綴和后綴對(duì)應(yīng)的位置匹配
  • 前綴和后綴對(duì)應(yīng)的位置不匹配
  • 前綴和后綴對(duì)應(yīng)的位置匹配
    由于第一個(gè)位置只有一個(gè)字符,不存在前綴和后綴一說(shuō),所以初值為-1,其他位置全部初始化為0。從第一個(gè)位置開(kāi)始遍歷。

    我們可以采用動(dòng)態(tài)規(guī)劃的方法,如果當(dāng)前位置的前綴和后綴匹配時(shí),則匹配前綴的長(zhǎng)度加一,而我們當(dāng)前的前綴[0, i]又是從[0, i - 1]推導(dǎo)而來(lái),所以它們之間的關(guān)系也就是next[i] = next[i - 1] + 1

    簡(jiǎn)而言之,如果當(dāng)前的字符匹配,則當(dāng)前next的值為上一個(gè)位置的值加一。

    前綴和后綴對(duì)應(yīng)的位置不匹配
    假設(shè)此時(shí)已匹配前綴為GTGTGC,此時(shí)GTGT與GTGC中的T和C并不相同,又如何處理呢?

    此時(shí)我們無(wú)法從上一個(gè)位置來(lái)推出這一個(gè)位置的值,而在這個(gè)壞字符出現(xiàn)之前,GTG又是一組可匹配的最長(zhǎng)前綴子串,所以我們可以將問(wèn)題GTGTGC轉(zhuǎn)換為求后綴GTGC的最長(zhǎng)可匹配前綴

    也就是將j給回溯到next[j]的位置


    但是由于T和C還是不相同,所以再求后綴GC的最長(zhǎng)可匹配前綴

    此時(shí)繼續(xù)回溯


    由于G不等于C,所以該位置沒(méi)有任何匹配的前綴,next為0

    簡(jiǎn)而言之,如果最長(zhǎng)可匹配后綴子串無(wú)法與前綴匹配,則嘗試尋找當(dāng)前的后綴子串中是否存在一個(gè)可匹配的前綴子串,將j回溯到next[j]的位置

    void getNext(const string& pattern, vector<int>& next) {int i , j = 0;next[0] = -1; //第一個(gè)位置不存在數(shù)據(jù),為-1for(int i = 1; i < next.size(); i++){//如果當(dāng)前位置沒(méi)有匹配前綴,則回溯到求當(dāng)前后綴的最長(zhǎng)可匹配前綴while(j != 0 && pattern[j] != pattern[i]){j = next[j];}//如果該位置匹配,則在next數(shù)組在上一個(gè)位置的基礎(chǔ)上加一if(pattern[j] == pattern[i]){j++;}next[i] = j;} }

    代碼實(shí)現(xiàn)

    KMP的完整實(shí)現(xiàn)如下

    #include<string> #include<iostream> #include<vector>using namespace std;//獲取next數(shù)組 void getNext(const string& pattern, vector<int>& next) {int i , j = 0;next[0] = -1; //第一個(gè)位置不存在數(shù)據(jù),為-1for(int i = 1; i < next.size(); i++){//如果當(dāng)前位置沒(méi)有匹配前綴,則回溯到求當(dāng)前后綴的最長(zhǎng)可匹配前綴while(j != 0 && pattern[j] != pattern[i]){j = next[j];}//如果該位置匹配,則在next數(shù)組在上一個(gè)位置的基礎(chǔ)上加一if(pattern[j] == pattern[i]){j++;}next[i] = j;} }int knuthMorrisPratt(const string& str, const string& pattern) {//不滿足條件則直接返回falseif(str.empty() || pattern.empty() || str.size() < pattern.size()){return -1;}int i = 0, j = 0;int len1 = str.size(), len2 = pattern.size();vector<int> next(pattern.size(), -1); //next數(shù)組表示第j - 1個(gè)位置的匹配前綴的起始下標(biāo)getNext(pattern, next);for(int i = 0; i < len1; i++){//找到最長(zhǎng)的可匹配前綴while(j > 0 && str[i] != pattern[j]){j = next[j - 1] + 1; //直接滑動(dòng)到匹配的前綴位置,并繼續(xù)匹配下一個(gè)位置}//如果匹配成功,則繼續(xù)匹配下一個(gè)位置if(str[i] == pattern[j]){j++;}if(j == len2){return i - len2 + 1; //返回主串中匹配子串的起始下標(biāo)}}return -1; }

    空間復(fù)雜度
    KMP算法需要借助到一個(gè)next數(shù)組,所以空間復(fù)雜度為O(M),M為模式串長(zhǎng)度
    時(shí)間復(fù)雜度
    時(shí)間復(fù)雜度主要包含兩個(gè)部分,next數(shù)組的構(gòu)建以及對(duì)主串的遍歷。
    其中next數(shù)組構(gòu)建的時(shí)間復(fù)雜度可以估算為O(M),第二步主串的循環(huán)為O(N)。所以KMP算法的時(shí)間復(fù)雜度為O(N + M),N為主串長(zhǎng)度,M為模式串長(zhǎng)度

    總結(jié)

    以上是生活随笔為你收集整理的字符串匹配算法(三):KMP(KnuthMorrisPratt)算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。