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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【数据结构与算法】字符串匹配 BM算法

發(fā)布時間:2023/12/10 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【数据结构与算法】字符串匹配 BM算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 單模式串匹配
    BF 算法和 RK 算法
    BM 算法和 KMP 算法
  • 多模式串匹配算法
    Trie 樹和 AC 自動機
  • BM算法

    BM算法的核心思想是通過將模式串沿著主串大踏步的向后滑動,從而大大減少比較次數,降低時間復雜度。而算法的關鍵在于如何兼顧步子邁得足夠大與無遺漏,同時要盡量提高執(zhí)行效率。這就需要模式串在向后滑動時,遵守壞字符規(guī)則與好后綴規(guī)則,同時采用一些技巧。

    壞字符規(guī)則

    從后往前逐位比較模式串與主串的字符,當找到不匹配的壞字符時,記錄模式串的下標值si,并找到壞字符在模式串中,位于下標si前的最近位置xi(若無則記為-1),si-xi即為向后滑動距離。(但是壞字符規(guī)則向后滑動的步幅還不夠大,于是需要好后綴規(guī)則。

    好后綴規(guī)則

    從后往前逐位比較模式串與主串的字符,當出現壞字符時停止。若存在已匹配成功的子串{u},那么在模式串的{u}前面找到最近的{u},記作{u’}。再將模式串后移,使得模式串的{u’}與主串的{u}重疊。若不存在{u’},則直接把模式串移到主串的{u}后面。為了沒有遺漏,需要找到最長的、能夠跟模式串的前綴子串匹配的,好后綴的后綴子串(同時也是模式串的后綴子串)。然后把模式串向右移到其左邊界,與這個好后綴的后綴子串在主串中的左邊界對齊。

    何時使用壞字符規(guī)則和好后綴規(guī)則呢?

    首先在每次匹配過程中,一旦發(fā)現壞字符,先執(zhí)行壞字符規(guī)則,如果發(fā)現存在好后綴,還要執(zhí)行好后綴規(guī)則,并從兩者中選擇后移距離最大的方案執(zhí)行。

    技巧:
    1.通過散列表實現,壞字符在模式串中下標位置的快速查詢。

    private static final int SIZE = 256; // 全局變量或成員變量 private void generateBC(char[] b, int m, int[] bc) {for (int i = 0; i < SIZE; ++i) {bc[i] = -1; // 初始化bc}for (int i = 0; i < m; ++i) {int ascii = (int)b[i]; // 計算b[i]的ASCII值bc[ascii] = i;} }public int bm(char[] a, int n, char[] b, int m) {int[] bc = new int[SIZE]; // 記錄模式串中每個字符最后出現的位置generateBC(b, m, bc); // 構建壞字符哈希表int i = 0; // i表示主串與模式串對齊的第一個字符while (i <= n - m) {int j;for (j = m - 1; j >= 0; --j) { // 模式串從后往前匹配if (a[i+j] != b[j]) break; // 壞字符對應模式串中的下標是j}if (j < 0) {return i; // 匹配成功,返回主串與模式串第一個匹配的字符的位置}// 這里等同于將模式串往后滑動j-bc[(int)a[i+j]]位i = i + (j - bc[(int)a[i+j]]); }return -1; }

    2.每次執(zhí)行好后綴原則時,都會計算多次能夠與模式串前綴子串相匹配的好后綴的最長后綴子串。為了提高效率,可以預先計算模式串的所有后綴子串,在模式串中與之匹配的另一個子串的位置。同時預計算模式串中(同長度的)后綴子串與前綴子串是否匹配并記錄。在具體操作中直接使用,大大提高效率。
    3.如何快速記錄模式串后綴子串匹配的另一個子串位置,以及模式串(相同長度)前綴與后綴子串石否匹配呢?先用一個suffix數組,下標值k為后綴子串的長度,從模式串下標為i(0~m-2)的字符為最后一個字符,查找這個子串是否與后綴子串匹配,若匹配則將子串起始位置的下標值j賦給suffix[k]。若j為0,說明這個匹配子串的起始位置為模式串的起始位置,則用一個數組prefix,將prefix[k]設為true,否則設為false。k從0到m(模式串的長度)于是就得到了模式串所有前綴與后綴子串的匹配情況。

    // b表示模式串,m表示長度,suffix,prefix數組事先申請好了 private void generateGS(char[] b, int m, int[] suffix, boolean[] prefix) {for (int i = 0; i < m; ++i) { // 初始化suffix[i] = -1;prefix[i] = false;}for (int i = 0; i < m - 1; ++i) { // b[0, i]int j = i;int k = 0; // 公共后綴子串長度while (j >= 0 && b[j] == b[m-1-k]) { // 與b[0, m-1]求公共后綴子串--j;++k;suffix[k] = j+1; //j+1表示公共后綴子串在b[0, i]中的起始下標}if (j == -1) prefix[k] = true; //如果公共后綴子串也是模式串的前綴子串} }

    BM算法完整版

    // a,b表示主串和模式串;n,m表示主串和模式串的長度。 public int bm(char[] a, int n, char[] b, int m) {int[] bc = new int[SIZE]; // 記錄模式串中每個字符最后出現的位置generateBC(b, m, bc); // 構建壞字符哈希表int[] suffix = new int[m];boolean[] prefix = new boolean[m];generateGS(b, m, suffix, prefix);int i = 0; // j表示主串與模式串匹配的第一個字符while (i <= n - m) {int j;for (j = m - 1; j >= 0; --j) { // 模式串從后往前匹配if (a[i+j] != b[j]) break; // 壞字符對應模式串中的下標是j}if (j < 0) {return i; // 匹配成功,返回主串與模式串第一個匹配的字符的位置}int x = j - bc[(int)a[i+j]];int y = 0;if (j < m-1) { // 如果有好后綴的話y = moveByGS(j, m, suffix, prefix);}//好后綴和壞字符中最長的i = i + Math.max(x, y);}return -1; }// j表示壞字符對應的模式串中的字符下標; m表示模式串長度 private int moveByGS(int j, int m, int[] suffix, boolean[] prefix) {int k = m - 1 - j; // 好后綴長度if (suffix[k] != -1) return j - suffix[k] +1;for (int r = j+2; r <= m-1; ++r) {if (prefix[m-r] == true) {return r;}}return m; }

    應用

    grep命令 文本編輯器查找功能

    總結

    以上是生活随笔為你收集整理的【数据结构与算法】字符串匹配 BM算法的全部內容,希望文章能夠幫你解決所遇到的問題。

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