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

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

生活随笔

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

编程问答

字符串匹配算法(一):BF(BruteForce)算法和RK(RabinKarp)算法

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

文章目錄

  • BF
    • 思路
    • 實(shí)現(xiàn)
  • RK
    • 思路
    • 實(shí)現(xiàn)


字符串匹配是計(jì)算機(jī)科學(xué)中最古老、研究最廣泛的問(wèn)題之一。一個(gè)字符串是一個(gè)定義在有限字母表∑上的字符序列。例如,ATCTAGAGA是字母表∑ = {A,C,G,T}上的一個(gè)字符串。字符串匹配問(wèn)題就是在一個(gè)大的字符串T中搜索某個(gè)字符串P的所有出現(xiàn)位置。

為了方便后面講解,在這里先提出幾個(gè)概念。

  • 主串和模式串主串即字符串的本體,模式串即用于比對(duì)的子串,即我們將要在主串中查找模式串。例如我們要在字符串A中查找字符串B,則A為主串,B為模式串。
  • 單模式匹配算法即在一個(gè)主串中匹配一個(gè)模式串,例如BF、RK、BM、KMP等算法就是單模式
  • 多模式匹配算法即在一個(gè)主串中匹配多個(gè)字符串,例如之前講過(guò)的Trie樹(shù),以及之后會(huì)寫的AC自動(dòng)機(jī)。

BF

思路

BF算法是Brute Force的縮寫,中文名叫做暴力匹配算法,故名思意,其算法簡(jiǎn)單粗暴,但對(duì)應(yīng)的性能也不高,算法邏輯如下。

如上圖,BF算法的思路就是從主串的每一個(gè)位置進(jìn)行對(duì)比,如果當(dāng)前位置模式串無(wú)法匹配,就移動(dòng)到下一個(gè)位置,繼續(xù)匹配

實(shí)現(xiàn)

該算法的實(shí)現(xiàn)十分簡(jiǎn)單,代碼如下

#include<string> #include<iostream>using namespace std;int bruteForce(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();while(i < str.size()){while(j < pattern.size()){//如果當(dāng)前不匹配if(str[i + j] != pattern[j]){i++; //主串移動(dòng)到下一個(gè)位置j = 0; //模式串回到起始位置break;}j++; //如果模式串當(dāng)前位置匹配,則繼續(xù)匹配下一個(gè)位置}//如果模式串全部匹配,則返回匹配的位置if(j == pattern.size()){return i;}}return -1; }

可以看出,這種算法存在著大量無(wú)意義的匹配,在最壞的情況下,我們需要依次匹配完主串中的所有位置,因此其時(shí)間復(fù)雜度高達(dá)O(N * M) N為主串長(zhǎng)度,M為模式串長(zhǎng)度。


RK

思路

那么有什么方法可以減少那些不必要的匹配,提高效率呢?這時(shí)候就可以巧妙地借助哈希算法來(lái)完成這個(gè)任務(wù)。

如果對(duì)于哈希不了解的可以查看我往期的博客
高級(jí)數(shù)據(jù)結(jié)構(gòu)與算法 | 哈希 :哈希沖突、負(fù)載因子、哈希函數(shù)、哈希表、哈希桶

RK算法的全稱是RabinKarp,是Rabin 和 Karp這兩位科學(xué)家在BF算法的基礎(chǔ)上,加上了哈希的思想后是實(shí)現(xiàn)的。

BF算法的主要缺陷在于模式串會(huì)去嘗試匹配主串的任何一個(gè)位置,并且其中每次的匹配都會(huì)去一個(gè)一個(gè)字符進(jìn)行對(duì)比,導(dǎo)致效率的下降。

而RK算法則想到了一個(gè)好方法,可以先進(jìn)行一個(gè)預(yù)匹配,即哈希值的匹配,如果哈希值不同則說(shuō)明字符串不同,沒(méi)有比較的意義,而當(dāng)哈希值相同時(shí),為了避免哈希沖突的情況,再進(jìn)行字符串的匹配即可。

由于哈希值只是一個(gè)整型,其比較起來(lái)的代價(jià)比起字符串大大的降低了

不過(guò),哈希值的計(jì)算也需要通過(guò)遍歷字符串來(lái)獲得,那么效率不是又變低了嗎?這時(shí),就可以通過(guò)合理的設(shè)計(jì)哈希函數(shù),來(lái)解決這個(gè)問(wèn)題。

在這里,我選擇使用最簡(jiǎn)單的按位相加來(lái)進(jìn)行舉例

首先,我們將模式串以及主串中第一個(gè)用來(lái)匹配的子串的哈希值計(jì)算出來(lái),進(jìn)行比較。

int hashFunc(const string& str) {int hashCode = 0;for(int i = 0; i < str.size(); i++){hashCode += (str[i] - 'a');}return hashCode; }

當(dāng)其不匹配時(shí),我們需要匹配主串的下一個(gè)位置,由于字符串發(fā)生變化,哈希值也應(yīng)該發(fā)生變化。

考慮到匹配子串的變化起始就是從主串中向后移動(dòng)一位,也就是刪除了最高位,增加了最低位,所以我們可以借助這個(gè)性質(zhì),進(jìn)行增量計(jì)算即減去首位的哈希值,加上末尾的哈希值,就可以避免每次都要重復(fù)計(jì)算哈希值的問(wèn)題。

//獲取移動(dòng)到下一個(gè)位置后的字符串哈希值,即減去開(kāi)頭的哈希值,加上末尾的哈希值 int nextHash(const string& str, int hash, int begin, int end) {hash -= (str[begin] - 'a');hash += (str[end] - 'a');return hash; }

上面這種哈希算法的計(jì)算十分簡(jiǎn)單,但是也存在著大量的哈希沖突,所以選擇一個(gè)合理的哈希算法,對(duì)效率的提升有很大的幫助,但是每種哈希算法也存在著一定的缺陷,例如26進(jìn)制,雖然其幾乎避免了哈希沖突,但是在字符串過(guò)長(zhǎng)時(shí)會(huì)導(dǎo)致哈希值的溢出,所以要結(jié)合使用場(chǎng)景對(duì)哈希函數(shù)進(jìn)行選擇。
由于這里只是算法的一個(gè)介紹,就直接使用這種最簡(jiǎn)單的哈希函數(shù)。

此時(shí),整個(gè)RK算法就包含兩個(gè)部分,第一部分是進(jìn)行哈希值的計(jì)算,時(shí)間復(fù)雜度為O(N),第二部分就是進(jìn)行字符串的匹配,因?yàn)楣V档谋容^的時(shí)間復(fù)雜度為O(1),而哈希值的更新也只是簡(jiǎn)單的增量計(jì)算,也是O(1),所以整個(gè)算法的時(shí)間復(fù)雜度為O(N),但是在最壞情況下,如存在大量的哈希沖突時(shí),每次都需要將進(jìn)行字符串的對(duì)比,這時(shí)的時(shí)間復(fù)雜度就會(huì)退化回O(N * M)

實(shí)現(xiàn)

算法實(shí)現(xiàn)如下

#include<string> #include<iostream>using namespace std;//字符串哈希函數(shù),這里使用的是最簡(jiǎn)單的按位相加 int hashFunc(const string& str) {int hashCode = 0;for(int i = 0; i < str.size(); i++){hashCode += (str[i] - 'a');}return hashCode; }//獲取移動(dòng)到下一個(gè)位置后的字符串哈希值,即減去開(kāi)頭的哈希值,加上末尾的哈希值 int nextHash(const string& str, int hash, int begin, int end) {hash -= (str[begin] - 'a');hash += (str[end] - 'a');return hash; }int rabinKarp(const string& str, const string& pattern) {//不滿足條件則直接返回falseif(str.empty() || pattern.empty() || str.size() < pattern.size()){return -1;}int len1 = str.size(), len2 = pattern.size();int patternHash = hashFunc(pattern);int subHash = hashFunc(str.substr(0, len2));for(int i = 0; i < (len1 - len2 + 1); i++){//如果當(dāng)前的哈希值相同,則遍歷比較字符串是否也相同,如果不同則沒(méi)有必要進(jìn)行比較if(patternHash == subHash && pattern == str.substr(i, len2)){return i; //如果當(dāng)前匹配,則返回匹配主串的起始位置}//如果主串中剩余字符還能與模式串進(jìn)行對(duì)比,則更新哈希值if(len1 - i > len2){subHash = nextHash(str, subHash, i, i + len2);}}return -1; }

總結(jié)

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

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