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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法一篇帮助你吃下KMP算法

發布時間:2023/12/4 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法一篇帮助你吃下KMP算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

模式匹配

  • 什么是模式匹配,我們用一個案例來說明:
    • 當S = “s1,s2,s3,s4 …sn” T=“t1,t2,t3,t4 … tn” 在字符串S中尋找T字符串的過程就是模式匹配的過程,T就說模式串,S是主串
  • 實現方案:
    • 暴力破解,逐字符判斷,直到找到對應的全匹配
    • 由暴力破解的缺點逐步優化,引出KMP算法

暴力匹配

  • 思路如下:

    • 令i 是目標串S的下標, j是模式串T的下標
    • 當匹配到Si == Tj 的時候,我們做 i++,j++繼續匹配下一個
    • 當匹配到Si != Tj 的時候,我們需要沖頭匹配,另 i = i - j + 1, j = 0
    • 其中 i = i - j + 1 就說目標串S的回朔,我們匹配過j 個字符,那么回退 j個位置到當前這次匹配的最開始的位置,并且先前移動一位。
  • 根據如上分析有如下代碼:

/*** Created by jiamin5 on 2022/2/12.* 給你兩個字符串 haystack 和 needle ,請你在 haystack 字符串中找出 needle 字符串出現的第一個位置(下標從 0 開始)。如果不存在,則返回 -1 。* 來源:力扣(LeetCode第28題)* 鏈接:https://leetcode-cn.com/problems/implement-strstr*/ public class StrKmp {public static void main(String[] args) {System.out.println(strBM("asdfuohadsuoufhuuowyqruoqiwhgbyur23iu4y2oi34hf", "uoq"));}/*** 模式匹配BM暴力破解寫法* */public static int strBM(String haystack, String needle){if (needle == null && haystack == null) {return 0;}if (haystack == null || haystack.length() <= 0) {return -1;}if (needle.isEmpty()) {return 0;}if (haystack.length() < needle.length()) {return -1;}char[] hasChar = haystack.toCharArray();char[] needChar = needle.toCharArray();int i = 0, j = 0;while (i<hasChar.length && j < needChar.length){if(hasChar[i] == needChar[j]){i++;j++;}else {i=i-j+1;j=0;}}if(j == needChar.length){return i-j;}return -1;} }
暴力破解算法分析
  • 我們用一個特殊一點的案例來解釋暴力破解算法
  • 依然用i 表示目標串S:ab abc abcac bab, 用j 表示模式串T:abcac, 有如下流程
  • 第一步,當Si, Tj 匹配到2 位置的時候,匹配是吧,i回朔到位置 2-2 +1 = 1,j=0
  • 第二次匹配第一個字符就失敗,j=1-0+1=2
  • 第三次匹配,一直匹配到i= 6 的時候才匹配失敗,這個時候,i=6-4+1 = 3,從第二個b開始匹配

  • 第四次從第二個b開始,顯然是不可匹配到

  • 依次一直持續到第六次匹配,才匹配成功

  • 總結
    • 在如上流程中,每次匹配失敗我們都回將i 回朔到當前匹配批次的下一個位置,j又從0開始比較
    • 在第三次匹配結束后,我們可以發現:i=3和j=0,i=4和j=0以及i=5和j=0是不必進行的
    • 關鍵點在于,我們在第三次匹配的時候,已經知道了前面的串中有ab abcab,從中我們可以得出,主串中第3,4,5個字符必然是‘b’,‘c’,‘a’,與模式串T中的 1,2,3 字符是相同的,是可以沒必要在匹配的
    • 可以在第三步驟的時候,就知道,我們應該直接移動到i = 6 的位置,因為i = 6 的位置與j = 0 的位置都是 a 是想匹配的,之前的匹配過的字符已經是已知不可匹配的
    • 依照如上分析:如果有一個類似的數組,用來輔助挑轉位置,那么明顯會加快進程。這樣就引出了我們的KMP算法,不回溯i,加快匹配效率。

KMP算法

  • KMP算法是一種改進的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人們稱它為克努特—莫里斯—普拉特操作(簡稱KMP算法)。KMP算法的核心是利用匹配失敗后的信息,盡量減少模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是通過一個next()函數實現,函數本身包含了模式串的局部匹配信息。KMP算法的時間復雜度O(m+n) [1] (自百度百科)

  • 算法流程如下:

    • 如果j = -1 ,i++,j++,繼續匹配下一個字符
    • 如果Si == Tj,i++, j++,繼續匹配下一個字符
    • 如果Si != Tj,且j != -1,i不變,j = next[j],意味著在匹配失敗的時候,接下來模式串T要相對于主串S向右移 j - next[j] 位置。
  • 那么會出現如下幾個問題:

  • next是什么
  • 怎么得到next
  • 位移信息next的確認
    • 要了解next的來源,先得知道一個名字:最長公共前后綴。假設有一個串P=“P0P1P2P3…Pj-1Pj”如果存在P0P1P2…Pk-1Pk = Pj-kPj-k+1…Pj-1Pj,我們就可以說在P串中最大長度為k+1 的公共前后綴
    • 找最大公共前后綴的意義:
      • 例如在意思的案例中:ab abcabcac bac,我們在第三步驟中匹配到第一個abc,到第六步驟中匹配到第二個abc,在中間四,五步驟都在確認該字符:abcab,其中他的最長公共前后綴長度是,也就是ab
      • 我們完全可以看出,單我們匹配到第一個abc的時候,完全可以直接跳過接下來的bc的匹配直接跳到第二個ab去匹配,那么最長公共前后綴就是這個作用,他告訴我們,在已經匹配過的字符串中,有沒有這樣的字符,我們直接跳過中間的字符,區匹配后面對于的字符ab就行了
    前后綴確認
    • 還是用剛才那個案例字符串P = abaabca

    • 這樣公共先后在最長長度會和P的每個字符有一個對應關系:

    • 我們從這個表引出next數組的值,next數組的值是除當前字符外的公共前后綴的長度,相當于將上表中數據向右移動一位,并且在前補充-1,得到如下

    • 接下來我們通過next數組來進行匹配推到,如下圖流程:

    • 第一步,i =0 ,j=0開始,當i = 2 ,j= 2 時候匹配失敗,那么i不動,next[j]=next[2]=0,S右移j - next[j] = 2位 , j 回朔到 0

    • 第二次匹配,i= 2開始,j=0, 當匹配到i= 6 j=4 時候,失敗,還是一樣 i不動 ,j = next[j]=next[4] = 1,也就是S向右移 j-next[j]=3位

    • 第三次匹配,i = 6 ,j = 1 檔i = 10, j = 5 時候匹配成功,返回 i-j=5

    結論:
    • 當主串S 與模式串P 匹配失敗時候,j = next[j],也就是P 右移動j-next[j]。
    • 當模式串P后綴Pj-kPj-k+1…Pj-1與 主串S的Si-kSi-k+1…Si-1匹配成功,但是Pj,Sj j匹配失敗時候,因為next[j] = k,相當于在不包含Pj的模式串中有最大長度為k的相同前后綴,也就是P0P1…Pk-1 = Pj-kPj-k+1…Pj-1
    • 所以我們令j = next[j],讓模式串 右移 j - next[j]位,使模式串 P0P1…Pk-1與主串。Si-kSi-k+1…Si-1對齊,讓Pk 與Si 繼續匹配,如下圖所示:
    代碼實現求next數組
    • 之前我們是通過推到的方式得到的next數組,當我們用代碼實現時候,需要有一定的規則

    • 當next[0] = -1,next[1] = 0這個容易得到

    • 那么當通用情況,當j > 1 時候,如果我們已知next[j], 那么next[j+1]的值是我們需要求解的

    • 有如下兩種情況:

      • 當Pk = Pj,next[j+1] = next[j] + 1 = k + 1,代表字符E 前面的模式串中有長度為k+1 的最長公共前后綴

      • 當Pk != Pj時候,說明p0p1…pk-1pk != pj-kpj-k+1…pj-1pj,這個時候ABC與ABD不相同,也就是E前面的模式串中沒有長度為k+1的最大公共前后綴,所以next[j+1] = next[j] + 1 不在適用,我們需要尋找更短的最大公共前綴。

      • 因為next數組存儲的是之前字符中最長公共前后綴,那么當我們pk ! = pj 的時候,我們需要找k之前的一個匹配,那么就說next[k] 的位置就是上圖中的D,我們需要匹配的是 Pnext[k] = Pj 是否成立,也就讓k = next[k] 然后在做比較。如果成立,那么next[j+1] = next[next[k]]+ 1,如果不成了,next[j+1] = 0

    • 如上步驟中我們找k 之前的位置next[k]處的前綴來判斷是否有更短前綴,那為什么我們將k = next[k]就能找到最短前后綴,如下圖中所示

    • 如上圖,在Pk!=Pj時候,k = next[k] 用 Pnext[k] 去根Pj 匹配,
    • 我們可以看到,Pj之前,有一段長度為k的已匹配串,在Pk之前也有一段藍色的已匹配串,說明Pk字符前有一段長度為next[k] 的最大公共前后綴(藍色標記)
    • 如果Pk != Pj,說明P0P1…Pk-1Pk != Pj-kPj-k+1…Pj-1Pj,那么我們需要向前著更短的最大公共前后綴
    • 因為此時Pk和Pnext[k] 前面的藍色串已經完成了匹配
    • 如果Pnext[k]能和Pj匹配,那么我們就找到了最大公共前后綴,否則我們需要再次向前找 Pnext[next[k]] 去根Pj繼續匹配,直到找到更短的最大公共前后綴,或者字符串已經找到最開始位置,那么長度就是 0

    代碼實現

    • 經過如上分析有如下代碼:
    /*** Created by jiamin5 on 2022/2/12.* 給你兩個字符串 haystack 和 needle ,請你在 haystack 字符串中找出 needle 字符串出現的第一個位置(下標從 0 開始)。如果不存在,則返回 -1 。* 來源:力扣(LeetCode第28題)* 鏈接:https://leetcode-cn.com/problems/implement-strstr*/ public class StrKmp {public static void main(String[] args) {System.out.println(strKmp("asdfuohadsuoufhuuowyqruoqiwhgbyur23iu4y2oi34hf", "uoq"));}/*** KMP算法是一種改進的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人們稱它為克努特—莫里斯—普拉特操作(簡稱KMP算法)。* KMP算法的核心是利用匹配失敗后的信息,盡量減少模式串與主串的匹配次數以達到快速匹配的目的。* 具體實現就是通過一個next()函數實現,函數本身包含了模式串的局部匹配信息。KMP算法的時間復雜度O(m+n)。** */public static int strKmp(String haystack, String needle){if (needle == null && haystack == null) {return 0;}if (haystack == null || haystack.length() <= 0) {return -1;}if (needle.isEmpty()) {return 0;}if (haystack.length() < needle.length()) {return -1;}char[] hasChar = haystack.toCharArray();char[] needChar = needle.toCharArray();int [] next = getNext(needChar);int i = 0, j = 0;while (i<hasChar.length && j < needChar.length){if(j == -1 || hasChar[i] == needChar[j]){i++;j++;}else {j=next[j];}}if(j == needChar.length){return i-j;}return -1;}public static int[] getNext(char[] needChar){int[] next = new int[needChar.length+1];int j = 0, k = -1;next[j] = k;while (j < needChar.length){if(k == -1 || needChar[j] == needChar[k]){j++;k++;next[j] = k;}else {k = next[k];}}return next;} }

    參考文獻

    • 大佬v_JULY_v 的文章,比我更詳細

    前期文章

    • 上一篇:數據結構與算法–求1~n能組成的所有二叉搜索樹的排列
    • 下一篇:數據結構與算法–力扣108提將有序數組轉換為二叉搜索樹

    總結

    以上是生活随笔為你收集整理的数据结构与算法一篇帮助你吃下KMP算法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 日本一二三不卡视频 | 欧美日韩在线二区 | 亚洲熟女乱色一区二区三区久久久 | 亚洲精品天堂成人片av在线播放 | 亚洲国产一区二区在线 | 午夜精品一二三区 | 成人3d动漫一区二区三区 | 午夜精品视频一区 | 波多野结衣亚洲视频 | 秋霞av在线| 国产高清视频网站 | 午夜影院在线观看18 | 日韩系列在线 | xxxxxhd亚洲人hd | 捆绑裸体绳奴bdsm亚洲 | 岛国毛片在线观看 | 精品在线视频一区二区 | 久久网站免费 | 国产视频一区在线播放 | 热99精品| 欧美日韩亚洲一区 | 激情五月综合网 | 黄网站在线观 | 欧美妞干网| www.四虎影视| 草久久久久 | 国产性―交―乱―色―情人 | 欧美在线观看网站 | 久久久久99精品成人片毛片 | 日韩午夜在线 | 久久精品视频1 | 国产av无码国产av毛片 | 99成人精品视频 | 国产的av| 欧美一区二区三区视频在线观看 | 欧美在线观看视频一区 | 爽爽淫人 | 777色| 久久久久久五月天 | 热久久最新 | 欧美激情视频一区二区三区在线播放 | 欧美大片一级 | 中文字幕日韩一区二区 | 国产爆乳无码一区二区麻豆 | 国产按摩一区二区三区 | 国产91页 | 神马伦理影视 | 男人天堂综合 | 国产一区二区三区视频在线观看 | 国产精品水嫩水嫩 | 国产乱女淫av麻豆国产 | 欧洲美一区二区三区亚洲 | 国产中文在线播放 | 美女黄色av | 公与妇乱理三级xxx www色 | 女上男下动态图 | 免费在线观看污网站 | 久草视频中文在线 | a在线播放 | 精产国品一二三产品蜜桃 | 黄色一级免费大片 | 综合精品久久 | 性欧美一区 | 裸体喂奶一级裸片 | 四虎在线免费观看视频 | 成人一级片在线观看 | 亚洲永久视频 | 波多野结衣之潜藏淫欲 | 丁香花国语版普通话 | 无遮挡毛片 | 亚洲中国色老太 | 中文字幕91在线 | 全黄性性激高免费视频 | 日韩三级成人 | 51久久久| 五十路在线观看 | 国产精品情侣自拍 | 日韩av网页 | 日韩欧美视频一区二区三区 | 97公开视频 | 免费瑟瑟网站 | 99久免费精品视频在线观78 | 美女张开腿让男人操 | 男人看片网站 | 台湾av在线播放 | 国产福利免费在线观看 | 国产黄色一级网站 | 青青草原国产视频 | 在线观看av的网站 | 国产免费av一区二区 | 视色影视 | 欧美福利在线观看 | www.夜夜 | 久久精品视频3 | 天天综合中文字幕 | 欧美日韩高清在线 | 欧美日韩一区在线播放 | 青青国产视频 | 国产精品揄拍一区二区 |