最近在寫一個搜索字符串的用例,恰好使用到這個算法,在網絡上學習了多篇文章,在這裏記錄一下本身的理解。算法
首先理清楚算法的原理:網絡
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);