子字符串查找(3)——BM算法

一陨界、BM算法定義

BM(Boyer-Moore)算法栏赴,它和KMP算法一樣都是從主串的最左端開始盈咳,然后不斷右移的耿眉。
與KMP算法的不同之處在于:BM算法從右向左掃描模式字符串,并將它和文本字符串比較鱼响。
BM算法的效率比KMP算法高很多鸣剪,常用于文本編輯器中的搜索匹配功能。

1-1 BM算法示例

二丈积、基本思想

2.1 壞字符與好后綴

BM算法中存在兩個概念:壞字符好后綴

2-1 壞字符與好后綴
壞字符(Bad Character)

所謂壞字符筐骇,就是模式串與文本串在從右往左的匹配過程中,出現(xiàn)的第1個不匹配的文本字符桶癣。(如圖2-1中的b)

那么拥褂,當匹配過程中出現(xiàn)“壞字符”如何處理?
應(yīng)當將模式字符串右移牙寞,直至遇到一個與壞字符相同的模式字符或最終移動到壞字符+1位置(即沒有遇到與壞字符匹配的模式字符),如下圖:

好后綴(Good Suffix)

所謂好后綴饺鹃,就是模式串與文本串在從右往左的匹配過程中莫秆,出現(xiàn)的部分匹配的文本字符串后綴(如圖2-1中的a、ba悔详、aba)

那么镊屎,當匹配過程中出現(xiàn)“好后綴”如何處理?
應(yīng)當將模式字符串右移茄螃,直至模式字符串與好后綴完全匹配或部分匹配或不匹配缝驳,如下圖:

綜上所述,在匹配過程中归苍,如果當前字符匹配失敗用狱,則:
模式字符串右移位數(shù) = Max { shift(壞字符) , shift(好后綴) }

2.2 算法示例

以字符串"HERE IS A SIMPLE EXAMPLE",模式字符串"EXAMPLE"為例拼弃。

  1. 首先夏伊,文本與模式串頭部對齊,從尾部開始比較吻氧,發(fā)現(xiàn)"S"與"E"不匹配溺忧,所以"S"是"壞字符",且不存在好后綴盯孙。
    由于模式串左側(cè)中不存在與壞字符“S”相同的字符鲁森,故模式字符串右移至"S"位置+1,shift('S')=7振惰,即模式字符串右移位數(shù) = Max { 7, 0}=7
  1. 再次從尾部開始比較歌溉,發(fā)現(xiàn)"P"與"E"不匹配,所以"P"是"壞字符"骑晶,且不存在好后綴研底。
    但是,模式串左側(cè)“EXAMPL”中存在相同的字符"P"透罢,故shift('P')=2,即模式字符串右移位數(shù) = Max { 2, 0}=2
  1. 依然從尾部開始比較冠蒋,"MPLE"與"MPLE"匹配羽圃,直到"I"和"A"不匹配,所以"I"是"壞字符"抖剿,且存在好后綴"MPLE"朽寞。
  1. 此時"I"和"A"不匹配,模式串左側(cè)“EX”中不存在與壞字符"I"相同的字符斩郎,故shift('I')=3脑融;
    模式字符串中存在與好后綴“MPLE”部分匹配的"E",故shift('E')=6缩宜,模式字符串右移位數(shù) = Max { 6, 3}=6
  1. 依然從尾部開始比較肘迎,"P"和"E"不匹配甥温,所以"P"是"壞字符",且不存在好后綴妓布。
    但是姻蚓,模式串左側(cè)“EXAMPL”中存在相同的字符"P",故shift('P')=2匣沼,即模式字符串右移位數(shù) = Max { 2, 0}=2
  1. 然后再次從尾部開始逐位比較狰挡,發(fā)現(xiàn)全部匹配,于是搜索結(jié)束释涛。

三加叁、算法實現(xiàn)

BM算法在從右向左的比較過程中,當某個字符失配時唇撬,需要分別按壞字符算法好后綴算法 計算模式串需要右移的距離它匕,然后取最大值,進行右移局荚。

3.1 壞字符算法

/**
 * 構(gòu)造壞字符數(shù)組 
 * bmBC['k']表示壞字符'k'在模式串中的最右位置超凳,如不存在則為-1
 */
public int[] getBmBc(String ptn) {
    int[] bmBc = new int[R]; // R為字母表大小
    for (int c = 0; c < R; c++) {
        bmBc[c] = -1;
    }
    for (int i = 0; i < ptn.length(); i++) {
        bmBc[ptn.charAt(i)] = i;
    }
    return bmBc;
}

位移數(shù)(Shift) = 壞字符在當前模式串中的索引 - bmBC[壞字符];
(如果結(jié)果小于0,則置為1耀态,因為模式串不能左移)

3.2 好后綴算法

1轮傍、構(gòu)建輔助數(shù)組suffix[]:
suffix[i] = s 表示字符P[i]之前的子串,與模式串后綴匹配的最大長度首装,如下圖所示创夜,用公式可以描述:滿足P[i-s, i] == P[m-s, m]的最大長度s。

 * 構(gòu)建輔助后綴數(shù)組
 * suffix[i] = n 表示模式字符P[i]之前的子串仙逻,與模式串后綴匹配的最大長度為n
 */
private int[] getSuffix(String ptn) {
    int m = ptn.length();
    int[] suffix = new int[m];
 
    suffix[m - 1] = m;
    for (int i = m - 2; i >= 0; i--) { // i指向當前求解的字符
        int n = 0; // 記錄相同后綴數(shù)量
        int k1 = i, k2 = m - 1;
        while (k1 >= 0 && k2 >= 0 && ptn.charAt(k1) == ptn.charAt(k2))
            n++;
        suffix[i] = n;
    }
    return suffix;
}

2驰吓、構(gòu)建好后綴數(shù)組bmGs[]:
bmGs[i]表示遇到好后綴時,模式串應(yīng)該右移的距離系奉,其中i表示當前匹配失效的字符位置(也就是當前壞字符的位置)檬贰。
分為三種情況:

  1. 模式串中任何字符與好后綴匹配(這種情況右移距離最長)
  1. 模式串中有前綴與好后綴部分匹配(這種情況右移距離次長)
  1. 模式串中有子串與好后綴完全匹配(這種情況右移距離最短)

綜上,我們應(yīng)該盡量讓一次移動的距離少一點缺亮,以免回退翁涤。

/**
 * 求解bmGs數(shù)組
 * bmGs[i]表示遇到好后綴時,模式串應(yīng)該右移的距離萌踱,其中i表示好后綴前面一個字符的位置(也就是當前壞字符的位置)
 */
private int[] getBmGs(String ptn) {
    int m = ptn.length();
    int[] bmGs = new int[m];
    int[] suffix = getSuffix(ptn);
 
    // 1.模式串中無任何字符與好后綴匹配(這種情況右移距離最長)
    for (int i = 0; i < m; i++) {
        bmGs[i] = m;
    }
 
    // 2.模式串中有前綴與好后綴部分匹配(這種情況右移距離次長)
    for (int i = m - 1; i >= 0; i--) {
        if (suffix[i] == i + 1) {
            for (int j = 0; j < m - 1 - i; j++) {
                if (bmGs[j] == m)
                    bmGs[j] = m - 1 - i;
            }
        }
    }
 
    // 3.模式串中有子串與好后綴完全匹配(這種情況右移距離最短)
    for (int i = 0; i < m - 1; i++) {
        bmGs[m - 1 - suffix[i]] = m - 1 - i;
    }
    return bmGs;
}

3.3 BM算法完整實現(xiàn)

public int search(String txt, String ptn) {
    int n = txt.length();
    int m = ptn.length();
 
    int[] bmBc = getBmBc(ptn); // 壞字符算法
    int[] bmGs = getBmGs(ptn); // 好后綴算法
 
    int i = 0, j = m - 1; // 指針i追蹤文本葵礼,指針j追蹤模式字符串
    while (i <= n - m) {
        j = m - 1; // 每次失配后,j重置為起始位置
        while (j >= 0 && ptn.charAt(j) == txt.charAt(i + j))
            j--;
 
        if (j < 0) // 完全匹配成功
            return i;
        else {
            char badChar = txt.charAt(i + j);  // i+j指向了當前正在比較的文本字符(壞字符)
            // 壞字符算法
            int len1 = j - bmBc[badChar] < 0 ? 1 : j - bmBc[badChar];
            // 好后綴算法
            int len2 = bmGs[badChar];
            // 取最大位移
            i += max(len1, len2); // 匹配失敗
        }
    }
    return -1;
}

四并鸵、性能分析

  • 預(yù)處理階段
    時間復(fù)雜度和空間復(fù)雜度都是O(M)

  • 搜索階段
    時間復(fù)雜度:O(MN)
    最好情況下:O(N/M)

注:N為文本串長度鸳粉,M為模式串長度

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市园担,隨后出現(xiàn)的幾起案子届谈,更是在濱河造成了極大的恐慌枯夜,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疼约,死亡現(xiàn)場離奇詭異卤档,居然都是意外死亡,警方通過查閱死者的電腦和手機程剥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門劝枣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人织鲸,你說我怎么就攤上這事舔腾。” “怎么了搂擦?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵稳诚,是天一觀的道長。 經(jīng)常有香客問我瀑踢,道長扳还,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任橱夭,我火速辦了婚禮氨距,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘棘劣。我一直安慰自己俏让,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布茬暇。 她就那樣靜靜地躺著首昔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪糙俗。 梳的紋絲不亂的頭發(fā)上勒奇,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音巧骚,去河邊找鬼撬陵。 笑死,一個胖子當著我的面吹牛网缝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蟋定,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼粉臊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了驶兜?” 一聲冷哼從身側(cè)響起扼仲,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤远寸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后屠凶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驰后,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年矗愧,在試婚紗的時候發(fā)現(xiàn)自己被綠了灶芝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡唉韭,死狀恐怖夜涕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情属愤,我是刑警寧澤女器,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站住诸,受9級特大地震影響驾胆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贱呐,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一丧诺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吼句,春花似錦锅必、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至远搪,卻和暖如春劣纲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谁鳍。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工癞季, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人倘潜。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓绷柒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涮因。 傳聞我的和親對象是個殘疾皇子废睦,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 字符串匹配KMP算法詳解 1. 引言 以前看過很多次KMP算法,一直覺得很有用养泡,但都沒有搞明白嗜湃,一方面是網(wǎng)上很少有...
    張晨輝Allen閱讀 2,385評論 0 3
  • ??在文本處理中奈应,關(guān)鍵字匹配是一個十分常用且重要的功能。關(guān)鍵字稱為模式串购披,在文本T中尋找模式串P出現(xiàn)的所有出現(xiàn)的位...
    老羊_肖恩閱讀 4,480評論 1 4
  • 由于是畢業(yè)后轉(zhuǎn)行的原因杖挣,所以本人在工作之前沒有系統(tǒng)的學過數(shù)據(jù)結(jié)構(gòu)、算法導(dǎo)論之類的課刚陡。說白了就是沒有這樣的底蘊惩妇,哈哈...
    張晨輝Allen閱讀 692評論 0 0
  • 參考文章 知乎:如何更好的理解和掌握 KMP 算法?從頭到尾徹底理解KMPKMP 算法(1):如何理解 KMP(原...
    Mjolnir1107閱讀 974評論 0 0
  • 一、定義 KMP(Knuth-Morris-Pratt)算法橘荠,其實是對暴力查找算法的優(yōu)化屿附。在暴力查找算法中,用于追...
    null12閱讀 802評論 0 0