Boyer Moore 算法總結

最近在寫一個搜索字符串的用例,恰好使用到這個算法,在網絡上學習了多篇文章,在這裏記錄一下本身的理解。算法

首先理清楚算法的原理:網絡

1.算法是從尾部開始比較字符串的,若是最後一個字符不匹配,且不匹配的字符不在模式串中,那麼能夠直接忽略這段比較內容, 這種狀況下效率最高。以下所示:學習

ABBDD
CCGGXABBDDTUUY
D != X 所以能夠直接將指針移動到
----------ABBDD
CCGGXABBDDTUUY。spa

若是不匹配的字符在模式串中,以下:
AXBDD
CCGGXABBDDTUUY 則移動以下:指針

------AXBDD
CCGGXABBDDTUUYcode

這些就是算法中所謂的「壞字符「的狀況。字符串

2.當算法匹配一部分,可是遇到不等的狀況時,以下所示:string

ABBDD
CXBDDABBDD
B != X ,這時有兩種方式移動指針。第一種就是按照「壞字符」的方式,忽略CX,即指針向後移動兩位。可是這樣,效率好像不過高,由於咱們知道BDD這三個字符是比較過的,如何運用這些已知信息呢。io

這就是算法的精髓所在了。咱們假設ABBDD能與CX以後的字符串匹配上,那麼AB必須等於BD。但這明顯是錯誤的,所以若是咱們能事先對模式串進行預處理,即記錄它自身的字符串的匹配信息,那麼咱們就能夠利用這些信息,判斷是否須要進一步比較的必要性了。ast

怎麼理解呢,如在ABBDD中以BDD爲後綴的串,僅僅出現了一次,這樣咱們就知道,若是某個字符串如CXBDD中與BDD部分匹配,若是僅僅把模式串移動兩位那就會致使AB須要和BDD比較,可是咱們已經預先知道BDD之出現一次,這樣就冗餘了,所以咱們能夠把指針向後移動5個字節,效率就比「壞字符」高多了。這也被稱爲「好後綴」。

再好比
ADDBBDD 與
CAFBADDBBDD

A!=B 可是DD在模式串中出現兩次,咱們把指針移動到最近一個上就變成,

--------ADDBBDD
CAFBADDBBDD

爲何要這麼移動呢?就是由於DD是匹配的,那麼移動到這個位置上,就有可能匹配上。固然例子中是故意寫出能夠找到的,實際狀況不必定能立刻匹配上。可是這樣作,確實能夠大大提高查找的效率。

明白了算法的出發點,那麼代碼理解起來就容易了。具體實現不在贅述。

下面是官方代碼通過包裝,變爲C++的風格,自寫自測可用,效果不錯哦!

#ifndef BM_SEARCH_H
#define BM_SEARCH_H

#include<vector>
#include<string>
#include<stdio.h>
#include<string.h>

using namespace std;

class CBMSearch
{
public:
    CBMSearch(const char*);//參數爲模式串
    int    searchPattern(const unsigned char*,int,vector<int>&);//尋找匹配串位置
private:
    void    calSuffixes();//用於輔助計算好後綴
    void    preGoodSuffixes();//計算好後綴的移動字節數
    void    preBadCode();//計算壞字符的移動字節數
private:
    string        m_strPattern;//模式串
    const    char*    m_pSource;//須要查找的內容
    vector<int>    m_vecSuff;
    vector<int>    m_vecGoodSuff;
    vector<int>    m_vecBadCode;
};

#endif
#include"CBMSearch.h"

CBMSearch::CBMSearch(const char* pszPattern):m_strPattern(pszPattern),
                         m_vecSuff(m_strPattern.length()),
                         m_vecGoodSuff(m_strPattern.length()),
                         m_vecBadCode(256)
{
    preGoodSuffixes();
    preBadCode();
}

void    CBMSearch::calSuffixes()
{
    int    nLastMatch =0,nFailedIndex =0;
    int    nPatternLen = m_strPattern.length();
    const char* x  = m_strPattern.c_str();

    m_vecSuff[nPatternLen -1] = nPatternLen;
    nFailedIndex = nPatternLen -1;

    for(int i= nPatternLen -2;i >= 0;--i)
    {
        if(i > nFailedIndex && m_vecSuff[nPatternLen-1-(nLastMatch-i)] < i - nFailedIndex)
            m_vecSuff[i] = m_vecSuff[nPatternLen-1-(nLastMatch-i)];
        else
        {
            if(i < nFailedIndex)
                nFailedIndex = i;

            nLastMatch = i;

            while( nFailedIndex >= 0 && 
                x[nFailedIndex] ==x[nPatternLen -1 -(nLastMatch - nFailedIndex)])
                nFailedIndex --;

            m_vecSuff[i] = nLastMatch - nFailedIndex;
        }
    }
}

void    CBMSearch::preGoodSuffixes()
{
    calSuffixes();

    int nPatternLen = m_strPattern.length();

    for(int i=0;i <nPatternLen;i++)
        m_vecGoodSuff[i] = nPatternLen;

    for(int i = nPatternLen -1;i >= 0;i--)
    {
        if(m_vecSuff[i] != i+1)
            continue;
        
        for(int j=0;j<nPatternLen -1-i;j++)
        {
            if(m_vecGoodSuff[j] == nPatternLen)
                m_vecGoodSuff[j] = nPatternLen -1 -i;
        }
    }

    for(int i = 0;i <= nPatternLen -2;i++)
        m_vecGoodSuff[nPatternLen - 1 - m_vecSuff[i]] = nPatternLen -(i+1);
}

void    CBMSearch::preBadCode()
{
    int    nPatternLen = m_strPattern.length();
    const char* x = m_strPattern.c_str();

    for(int i =0;i<256;i++)
        m_vecBadCode[i] = nPatternLen;

    for(int i=0;i< nPatternLen -1;i++)
        m_vecBadCode[x[i]] = nPatternLen - i -1;
}

int    CBMSearch::searchPattern(const unsigned char* pContent,int nTotalLen,vector<int>& vecResult)
{
    int    nCurrent =0;
    int    nPatternLen = m_strPattern.length();
    int    i = 0;
    const char* x = m_strPattern.c_str();

    while(nCurrent <= nTotalLen - nPatternLen)
    {
        for(i = nPatternLen -1; i >=0 && x[i] == pContent[i+nCurrent];i--);

        if(i < 0)
        {
            vecResult.push_back(nCurrent);
            nCurrent+= nPatternLen;
        }
        else
        {
            int    nTmp = m_vecBadCode[pContent[i+nCurrent]] -(nPatternLen -i -1);
            
            if(nTmp > m_vecGoodSuff[i])
                nCurrent+=nTmp;
            else
                nCurrent+=m_vecGoodSuff[i];
        }
    }
    
    return 0;
}

使用方式以下:vector<int> ovecResult;//記錄找到的位置CBMSearch otest("ABBDD");otest.searchPattern("CAFBADDBBDD",strlen("CAFBADDBBDD"),ovecResult);

相關文章
相關標籤/搜索