91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

c++字符串匹配的知識點有哪些

發布時間:2022-03-17 10:02:47 來源:億速云 閱讀:159 作者:iii 欄目:大數據

今天小編給大家分享一下c++字符串匹配的知識點有哪些的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

字符串匹配

我們在字符串 A 中查找字符串 B,那字符串 A 就是主串,字符串 B 就是模式串。我們把主串的長度記作 n,模式串的長度記作 m。因為我們是在主串中查找模式串,所以 n>m。

單模式串匹配的算法:也就是一個串跟一個串進行匹配

BF 算法中的 BF 是 Brute Force 的縮寫,中文叫作暴力匹配算法,也叫樸素匹配算法。我們在主串中,檢查起始位置分別是 0、1、2…n-m 且長度為 m 的 n-m+1 個子串,看有沒有跟模式串匹配的。BF 算法的時間復雜度很高,是 O(n*m)

實際的開發中,它卻是一個比較常用的字符串匹配算法,絕大部分情況下,樸素的字符串匹配算法就夠用了。

  • 實際的軟件開發中,大部分情況下,模式串和主串的長度都不會太長。

  • 樸素字符串匹配算法思想簡單,代碼實現也非常簡單。滿足KISS(Keep it Simple and Stupid)設計原則

  •  

RK 算法的全稱叫 Rabin-Karp 算法,是由它的兩位發明者 Rabin 和 Karp 的名字來命名的。思路是這樣的:我們通過哈希算法對主串中的 n-m+1 個子串分別求哈希值,然后逐個與模式串的哈希值比較大小。如果某個子串的哈希值與模式串相等,那就說明對應的子串和模式串匹配了(無哈希沖突)。如果有哈希沖突,再對比一下子串和模式串本身。

RK 算法的效率要比 BF 算法高,RK 算法整體的時間復雜度就是 O(n)。不過這樣的效率取決于哈希算法的設計方法,如果存在沖突的情況下,時間復雜度可能會退化。極端情況下,哈希算法大量沖突,時間復雜度就退化為 O(n*m)。

要求:設計hash算法。找出兩個子串 s[i-1] 和 s[i]關系(其實就是abcde, bad表示相同子串, a與e的關系)。

BM (Boyer-Moore)算法:它的性能是著名的KMP 算法的 3 到 4 倍。當遇到不匹配的字符時,找規律,將模式串往后多滑動幾位。

BM 算法包含兩部分,分別是壞字符規則(bad character rule)和好后綴規則(good suffix shift)。

壞字符規則:從模式串的末尾往前倒著匹配,當我們發現某個字符沒法匹配的時候。我們把這個沒有匹配的字符叫作壞字符(主串中的字符)

當發生不匹配的時候,我們把壞字符對應的模式串中的字符下標記作 si。如果壞字符在模式串中存在,我們把這個壞字符在模式串中(最靠后的一個)的下標記作 xi。如果不存在,我們把 xi 記作 -1。那模式串往后移動的位數就等于 si-xi。(注意,我這里說的下標,都是字符在模式串的下標)。

c++字符串匹配的知識點有哪些

需要bc數組,記錄模式串所有字符位置。

缺點:單純使用壞字符規則還是不夠的。因為根據 si-xi 計算出來的移動位數,有可能是負數,比如主串是 aaaaaaaaaaaaaaaa,模式串是 baaa。

好后綴規則

c++字符串匹配的知識點有哪些

c++字符串匹配的知識點有哪些

  • 當好后綴{u}在模式串中查找,如果找到了另一個跟{u}相匹配的子串{u*},那我們就將模式串滑動到子串{u*}與主串中{u}對齊的位置。

  • 當好后綴{u}在模式串中查找,不存在另一個跟{u}相匹配的子串{u*},不能過度滑動到主串中{u}的后面。而是,從好后綴的后綴子串中,找一個最長的并且能跟模式串的前綴子串匹配的,假設是{v},然后將模式串滑動到如圖所示的位置。

c++字符串匹配的知識點有哪些

abc 的后綴子串就包括 c, bc。 abc 的前綴子串有 a,ab。

算法實現:

壞字符規則實現

  • 利用散列表存儲模式串每個字符位置下標,相同字符,記錄最后出現下標

  • 起始下標i,模式串從后往前匹配,遇到壞字符。找到壞字符對應模式串中的下標是si, 找到壞字符在模式串中最后出現位置xi (利用散列表,key=字符,value=下標,沒有計為-1),

     計算滑動i = i + (j - bc[(int)a[i+j]]);

好后綴規則實現(壞字符不在模式串m中最后一個位置

  • 預處理模式串,用數組存。每個數組元素記錄另一個可匹配后綴子串的位置。如下圖suffix[1],表示長度為1的子串,最后匹配的子串下標為2.

  • 除了 suffix 數組之外,我們還需要另外一個 boolean 類型的 prefix 數組,來記錄模式串的后綴子串是否能匹配模式串的前綴子串

c++字符串匹配的知識點有哪些

如何來計算并填充這兩個數組的值?

拿下標從 0 到 i 的子串(i 可以是 0 到 m-2)與整個模式串,求公共后綴子串。如果公共后綴子串的長度是 k,那我們就記錄 suffix[k]=j(j 表示公共后綴子串的起始下標)。如果 j 等于 0,也就是說,公共后綴子串也是模式串的前綴子串,我們就記錄 prefix[k]=true。

private void generateGS(char[] b, int m, int[] suffix, boolean[] prefix) {  

    // 初始化  

    for (int i = 0; i < m; ++i) {  

        suffix[i] = -1;  

        prefix[i] = false;  

    }  

    // abcde的好后綴是bcde,所以要減去一  

    // 前綴串b[0,m-2]與后綴串b[1,m-1]求公共后綴子串,以b[m-1] 為比較  

    // 比如cab 與b 求后綴子串  

    for (int i = 0; i < m - 1; i++) {  

        int j = i;  

        int k = 0;  

        while (j >= 0 && b[j] == b[m - 1 - k]) {  

            ++k;  

            suffix[k] = j;  

            --j;  

        }  

        if (j == -1) {  

            prefix[k] = true;  

        }  

    }  

}  

abc 的后綴子串就包括 c, bc。后綴子串必須有最后一個值。 abc 的前綴子串有 a,ab。前綴子串必須有第一個值

求前綴子串與后綴子串的公共后綴子串,并記錄前綴子串是否可完全匹配。

  • 通過好后綴長度k,判斷,是否存在另一匹配后綴。suffix[k]!=-1, 滑動j-x+1位。

c++字符串匹配的知識點有哪些

  • 好后綴{u}, 不在模式串中存在時。就需要找,后綴子串中是否有匹配的前綴子串。j為壞字符下標,循環找(r<=m-1)好后綴的后綴子串為b[r,m-1],r=j+2(是因為求好后綴的后綴子串) ,長度 k=m-r,判斷是否匹配前綴串,如果prefix[k]=true,滑動r位。

  • 如果兩條規則都沒有找到可以匹配好后綴及其后綴子串的子串,我們就將整個模式串后移 m 位。

平均的情況時間復雜度O(n/m),空間復雜度bc[], prefix[],suffix[]  O(m+bc.length)

因為好后綴和壞字符規則是獨立的,如果我們運行的環境對內存要求苛刻,可以只使用好后綴規則,不使用壞字符規則,這樣就可以避免 bc 數組過多的內存消耗。不過,單純使用好后綴規則的 BM 算法效率就會下降一些了。

實際上,我前面講的 BM 算法是個初級版本。為了讓你能更容易理解,有些復雜的優化我沒有講。基于我目前講的這個版本,在極端情況下,預處理計算 suffix 數組、prefix 數組的性能會比較差。比如模式串是 aaaaaaa 這種包含很多重復的字符的模式串,預處理的時間復雜度就是 O(m^2)。當然,大部分情況下,時間復雜度不會這么差。關于如何優化這種極端情況下的時間復雜度退化,如果感興趣,你可以自己研究一下。

實際上,BM 算法的時間復雜度分析起來是非常復雜,這篇論文“A new proof of the linearity of the Boyer-Moore string searching algorithm”證明了在最壞情況下,BM 算法的比較次數上限是 5n。這篇論文“Tight bounds on the complexity of the Boyer-Moore string matching algorithm”證明了在最壞情況下,BM 算法的比較次數上限是 3n。你可以自己閱讀看看。

KMP 算法基本原理

KMP 算法是根據三位作者(D.E.Knuth,J.H.Morris 和 V.R.Pratt)的名字來命名的,算法的全稱是 Knuth Morris Pratt 算法,簡稱為 KMP 算法。

在模式串和主串匹配的過程中,把不能匹配的那個字符仍然叫作壞字符,把已經匹配的那段字符串叫作好前綴從前往后匹配

c++字符串匹配的知識點有哪些

KMP 算法就是在試圖尋找一種規律:在模式串和主串匹配的過程中,當遇到壞字符后,對于已經比對過的好前綴,能否找到一種規律,將模式串一次性滑動很多位?

思路:遇到壞字符,找好前綴最長匹配前綴子串(好前綴的后綴子串與好前綴的前綴子串匹配)最后下標加1等于j,開始j與i 匹配。如果沒有最長匹配前綴子串,j = 0, j 和i 繼續比較, i 繼續++。

查找好前綴的后綴子串中,最長匹配的那個好前綴的前綴子串{v},長度是 k .我們把模式串一次性往后滑動 j-k 位,相當于,每次遇到壞字符的時候,我們就把 j 更新為 k,i 不變,然后繼續比較。

c++字符串匹配的知識點有哪些

為了表述起來方便,我把好前綴的所有后綴子串中,最長的可匹配前綴子串的那個后綴子串,叫作最長可匹配后綴子串;對應的前綴子串,叫作最長可匹配前綴子串

最長可匹配后綴子串最長可匹配前綴子串是相同的,只是位置不一樣,如下圖

c++字符串匹配的知識點有哪些

如何求好前綴的最長可匹配前綴最后字符下標呢?

c++字符串匹配的知識點有哪些

找到模式串的所有好前綴與自身比較(求最長可匹配前綴,[前綴后綴相匹配],求得next數組,數組的下標是每個好前綴結尾字符下標,數組的值是這個好前綴最長可以匹配前綴子串的結尾字符下標兩個下標

next算法思路

c++字符串匹配的知識點有哪些

可以利用next[i-1] 求next[i].

考察完所有的 b[0, i-1] 的可匹配后綴子串 b[y, i-1], 如果它對應的前綴子串的下一個字符等于 b[i],那這個 b[y, i] 就是 b[0, i] 的最長可匹配后綴子串。

ababacd為例,char數組為arr當i=5, arr[i] =  'c'  , 此時k = 2  ,  arr[k+1]!=arr[i]  不匹配, 找arr[0,i-1] 中次長可匹配子串最后下標位置。可以通過在arr[0,i-1]中0到最長可以匹配前綴子串的結尾字符下標next[k]找, 最長可匹配前綴下標,再判斷。

// b 表示模式串,m 表示模式串的長度  ababacd  

private  int[] getNexts(char[] b, int m) {  

    int[] next = new int[m];  

    next[0] = -1; // 好前綴為一個字符,省去操作,記為-1  

    int k = -1;  

    for (int i = 1; i < m; ++i) {  

        while (k != -1 && b[k + 1] != b[i]) {  

            k = next[k]; // 巧妙  

        }  

        // 好前綴, 前綴字符和后綴字符   

        // aa next[1]=0    

        // aba next[2] = 0  | abab next[3] = 1 | ababa next[4]=2  

        if (b[k + 1] == b[i]) {  

            ++k;  

        }  

        // i 為好前綴結尾下標字符  

        // next[i] 表示最長可以匹配前綴子串的結尾字符下標  

        next[i] = k;  

    }  

    return next;  

}  

{  上面理解了,這里就不必看了

利用已經計算出來的 next 值,我們是否可以快速推導出 next[i] 的值呢?

如果 next[i-1]=k-1(最長可匹配前綴子串最后下標),也就是說,子串 b[0, k-1] 是 b[0, i-1] 的最長可匹配前綴子串。如果子串 b[0, k-1] 的下一個字符 b[k],與 b[0, i-1] 的下一個字符 b[i] 匹配,那子串 b[0, k] 就是 b[0, i] 的最長可匹配前綴子串。

如果 b[0, k-1] 的下一字符 b[k] 跟 b[0, i-1] 的下一個字符 b[i] 不相等呢?這個時候就不能簡單地通過 next[i-1] 得到 next[i] 了。這個時候該怎么辦呢?

我們假設 b[0, i] 的最長可匹配后綴子串是 b[r, i]。如果我們把最后一個字符去掉,那 b[r, i-1] 肯定是 b[0, i-1] 的可匹配后綴子串,但不一定是最長可匹配后綴子串,如abaabab。

既然 b[0, i-1] 最長可匹配后綴子串對應的前綴子串的下一個字符并不等于 b[i],那么我們就可以考察 b[0, i-1] 的次長可匹配后綴子串 b[x, i-1] 對應的可匹配前綴子串 b[0, i-1-x] 的下一個字符 b[i-x] 是否等于 b[i]。如果等于,那 b[x, i] 就是 b[0, i] 的最長可匹配后綴子串。

}

KMP 算法只需要一個額外的 next 數組,數組的大小跟模式串相同。所以空間復雜度O(m),m 表示模式串的長度。next 數組計算的時間復雜度是 O(m)。

所以,綜合兩部分的時間復雜度,KMP 算法的時間復雜度就是 O(m+n)。

多模式串匹配算法:也就是在一個串中同時查找多個串

利用:trie 樹 

ac自動機

AC 自動機算法,全稱是 Aho-Corasick 算法。AC 自動機實際上就是在 Trie 樹之上,加了類似 KMP 的 next 數組,只不過此處的 next 數組是構建在樹上罷了。

AC 自動機的構建,包含兩個操作:

  • 將多個模式串構建成 Trie 樹;

  • 在 Trie 樹上構建失敗指針(相當于 KMP 中的失效函數 next 數組)。要構建每個節點的失敗指針,需要一個隊列。

Trie 樹中的每一個節點都有一個失敗指針。p 的失敗指針就是從 root 走到紫色節點形成的字符串 abc,跟所有模式串前綴匹配的最長可匹配后綴子串,就是箭頭指的 bc 模式串。

字符串 abc 的后綴子串有兩個 bc,c,我們拿它們與其他模式串匹配,如果某個后綴子串可以匹配某個模式串的前綴,那我們就把這個后綴子串叫作可匹配后綴子串。

c++字符串匹配的知識點有哪些  后綴子串(bc,c )跟模式串的前綴匹配

規律:如果我們把樹中相同深度的節點放到同一層,那么某個節點的失敗指針只有可能出現在它所在層的上一層。

我們可以像 KMP 算法那樣,當我們要求某個節點的失敗指針的時候,我們通過已經求得的、深度更小的那些節點的失敗指針來推導。也就是說,我們可以逐層依次來求解每個節點的失敗指針。所以,失敗指針的構建過程,是一個按層遍歷樹的過程。

首先 root 的失敗指針為 NULL,也就是指向自己。當我們已經求得某個節點 p 的失敗指針之后,如何尋找它的子節點的失敗指針呢?

我們假設節點 p 的失敗指針指向節點 q,我們看節點 p 的子節點 pc 對應的字符,是否也可以在節點 q 的子節點中找到。如果找到了節點 q 的一個子節點 qc,對應的字符跟節點 pc 對應的字符相同,則將節點 pc 的失敗指針指向節點 qc。

c++字符串匹配的知識點有哪些

如果節點 q 中沒有子節點的字符等于節點 pc 包含的字符,則令 q=q->fail(fail 表示失敗指針,這里有沒有很像 KMP 算法里求 next 的過程?),繼續上面的查找,直到 q 是 root 為止,如果還沒有找到相同字符的子節點,就讓節點 pc 的失敗指針指向 root。

c++字符串匹配的知識點有哪些  c++字符串匹配的知識點有哪些

如何在 AC 自動機上匹配主串?

主串遍歷,在root每一個分支匹配,當某個分支匹配了一部分如a->b->c,下一個d沒匹配到,就用失敗指針c = c ->fail  (失敗指針指向的位置,前面都是匹配的。),再接著匹配。

AC 自動機實現的敏感詞過濾系統,是否比單模式串匹配方法更高效呢?

首先,我們需要將敏感詞構建成 AC 自動機,包括構建 Trie 樹以及構建失敗指針。Trie 樹構建的時間復雜度是 O(m*len),其中 len 表示敏感詞的平均長度,m 表示敏感詞的個數。那構建失敗指針的時間復雜度是多少呢?

假設 Trie 樹中總的節點個數是 k,每個節點構建失敗指針的時候,(你可以看下代碼)最耗時的環節是 while 循環中的 q=q->fail,每運行一次這個語句,q 指向節點的深度都會減少 1,而樹的高度最高也不會超過 len,所以每個節點構建失敗指針的時間復雜度是 O(len)。整個失敗指針的構建過程就是 O(k*len)。

用 AC 自動機做匹配的時間復雜度是多少?

跟剛剛構建失敗指針的分析類似,for 循環依次遍歷主串中的每個字符,for 循環內部最耗時的部分也是 while 循環,而這一部分的時間復雜度也是 O(len),所以總的匹配的時間復雜度就是 O(n*len)。因為敏感詞并不會很長,而且這個時間復雜度只是一個非常寬泛的上限,實際情況下,可能近似于 O(n),所以 AC 自動機做敏感詞過濾,性能非常高。

以上就是“c++字符串匹配的知識點有哪些”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

政和县| 崇文区| 南川市| 大方县| 临清市| 太白县| 晴隆县| 嵊州市| 富锦市| 安徽省| 阳江市| 青铜峡市| 波密县| 福泉市| 新疆| 阿城市| 江都市| 泽库县| 小金县| 华容县| 鹤峰县| 郎溪县| 万全县| 台安县| 会昌县| 林甸县| 蒲江县| 慈溪市| 会同县| 彭水| 安义县| 刚察县| 修水县| 娄底市| 海阳市| 莱州市| 涿鹿县| 昂仁县| 民权县| 广灵县| 岳池县|