最近在写一个搜索字符串的用例,恰好使用到这个算法,在网络上学习了多篇文章,在这里记录一下本身的理解。算法
首先理清楚算法的原理:网络
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);