Chinaunix首页 | 论坛 | 博客
  • 博客访问: 20825
  • 博文数量: 4
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 65
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-31 23:33
文章分类

全部博文(4)

文章存档

2014年(4)

我的朋友

分类: C/C++

2014-01-05 13:55:12

    各种文本编辑器的“查找”功能(Ctrl + F),大多采用Boyer-Moore字符串匹配算法,它不仅效率高,并且构思奇妙,容易理解。1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了这种算法。
  首先,先阐述一下此算法的核心思想(算法讲解部分源自阮一峰的网络日志http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html
1.
    
      
    文本字符串为"HERE IS A SIMPLE EXAMPLE",待搜索的字符串为"EXAMPLE"
2.
    
      首先将待搜索字符串的头部与文本对齐,然后从尾部开始比较,发现尾部字符都不匹配("S"和"E"),那么说明此时文本对应的字符串肯定不是我们想要的,可以直接将"EXAMPLE"字符串向后移动,那么移动的步数就是算法要解决的核心问题,这时可以按照所谓的“坏字符规则” ,比如上图中的情况,"S"是坏字符,它对应待搜索字符串中的位移是6(按照位移来算,"EXAMPLE"的 第一个字符"E"位移为0,以此类推),如果在待搜索字符串中坏字符"S"在前面还出现过,则移动位数为:坏字符位置-其在搜索串中的上一次出现位置。如果没有,则移动位数为:坏字符位置-(-1)。所以"坏字符"规则为:
      后移步数 = 坏字符位置 - 其在搜索串中上一次出现位置                                               
      比如上图,"S"在"EXAMPLE"中没有出现,那么我们应该移动6-(-1)=7步。如下图:      
3.
    
      此时"P"是坏字符,按照坏字符规则,"EXAMPLE"向后移动6-4=2步
4.
    
      依然从尾部开始匹配,发现"MPLE"都匹配成功,坏字符为"I",按照坏字符规则搜索串应该向后移动2-(-1)=3步,但是此时有没有更好的移动方法呢?这就引出了所谓的“好后缀规则”。好后缀规则应该说包括3条规则:
   (1)“好后缀”的位置以最后一个字符为准。假定"ABCDEF"中"EF"是个好后缀,则其位置为"F"的位置,即为5
   (2)如果“好后缀”在搜索串中不只出现过一次,那么移动步数为:好后缀位置 - 其在搜索串中上一次出现的位置。                                   
   (3)如果"好后缀"在搜索串中只出现过一次,并且除最长的那个好后缀外(本身除外),如果有“好后缀”的“好后缀”(本文叫好后缀子缀)出现在头部(注意必须只能出现在头部),则移动位数为:好后缀位置 -  其好后缀子缀位置。如果没有好后缀子缀,则移动位数为:好后缀位置 - (-1)
      所以如果按照上述的“好后缀”规则,则移动步数应该为6-0=6步,因为好后缀"PLE"的好后缀子缀"E"出现在了"EXAMPLE"头部。Boyer-Moore算法指出每次待搜索串移动步数应该为坏字符规则和好后缀规则得出的移动步数的较大值,这保证了此算法能够又快又准确地进行字符串匹配。样例中按照好后缀规则移动6步后如下图:
5.
      
         继续匹配发现出现坏字符"P",按照“坏字符”规则移动步数为6-4=2步,如下图:
6.
      
         这样就完成了最终的匹配。应当指出,Boyer-Moore算法之所以得到普遍使用,最重要的原因是其搜索需要求解的移动步数只与待搜索字符串有关,这样我们就能通过提前建立一个“坏字符规则-步数映射表” 和“好后缀规则-步数映射表” ,当进行匹配的时候直接查表就可以获得结果。下面实现中就建立了“好后缀规则-步数映射表”,但没有建立“坏字符规则-步数映射表”,因为我觉得对于验证性的非工程项目所需的程序完全没有必要建立一个完整的“坏字符规则-步数映射表”,只需要根据坏字符每次求解一下移动步数返回给调用程序即可。
                                                      

程序设计如下:              
               
                                                                                      
  
 
/*main.cpp*/
  1. #include "CLStrMatching.h"
  2. #include "PrintError.h"

  3. int main(int argc , char**argv)
  4. {
  5.     if(argc != 3)
  6.     {
  7.         PrintError(AT , "argc is invalid");
  8.         exit(0);
  9.     }
  10.     CLStrMatching* temp = new CLStrMatching(argv[1] , argv[2]);
  11.     temp->startMatch();
  12.     delete temp;
  13.     return 0;
  14. }
/*CLStrMatching.h*/

  1. #ifndef STRMATCHING_H
  2. #define STRMATCHING_H

  3. #include <string>

  4. #include "CLWRuleProducer.h"

  5. #define MAX(x , y) ((x>y)?(x):(y))

  6. class CLStrMatching
  7. {

  8. public:

  9.     CLStrMatching(std::string , std::string);
  10.     ~CLStrMatching();
  11.     int readFile();
  12.     int startMatch();

  13. private:

  14.     std::string m_Context;
  15.     std::string m_SearchStr;
  16.     const std::string m_FilePath;
  17.     const unsigned int m_SeaStrTailOffset;
  18.     unsigned int m_StrCurOffset;//for search word
  19.     unsigned int m_ContextCurOffset;
  20.     const unsigned int m_SeaStrHeadOffset;

  21.     CLWRuleProducer* m_pWRuleProducer;
  22. };

  23. #endif
/*CLStrMatching.cpp*/

  1. #include <fstream>
  2. #include <iostream>

  3. #include "CLStrMatching.h"
  4. #include "PrintError.h"

  5. CLStrMatching::CLStrMatching(std::string search , std::string filepath):m_SearchStr(search)
  6. , m_FilePath(filepath) , m_SeaStrTailOffset(search.length() - 1) ,
  7. m_StrCurOffset(m_SeaStrTailOffset) ,m_ContextCurOffset(m_StrCurOffset)
  8. , m_SeaStrHeadOffset(0) , m_pWRuleProducer(new CLWRuleProducer(m_SearchStr))
  9. {

  10. }

  11. CLStrMatching::~CLStrMatching()
  12. {
  13.     if(m_pWRuleProducer != 0)
  14.         delete m_pWRuleProducer;
  15. }


  16. int CLStrMatching::readFile()
  17. {
  18.     std::fstream filein;
  19.     filein.open(m_FilePath.c_str());
  20.     if(!filein.is_open())
  21.     {
  22.         PrintError(AT , "open file failed");
  23.         filein.clear();
  24.         exit(0);
  25.     }
  26.     std::getline(filein , m_Context);
  27.     filein.close();
  28.     return 0;
  29. }

  30. int CLStrMatching::startMatch()
  31. {
  32.     readFile();
  33.     m_pWRuleProducer->init();

  34.     int initial = -1;//just initialize initial var with an impossible value

  35.     bool flag = true;//flag stands for if we have find an offset in context for
  36.     //a given string or not . True:have found one , False:not have found

  37.     while(m_ContextCurOffset <= m_Context.length() - 1)
  38.     {
  39.         initial = m_ContextCurOffset;
  40.         while(m_StrCurOffset >= m_SeaStrHeadOffset)
  41.         {
  42.             if(m_SearchStr[m_StrCurOffset] == m_Context[m_ContextCurOffset])
  43.             {
  44.                 if(m_StrCurOffset == 0)
  45.                     break;
  46.                 --m_StrCurOffset;
  47.                 --m_ContextCurOffset;
  48.             }
  49.             else
  50.             {
  51.                 int tmp = m_pWRuleProducer->pBadCharRule(m_Context[m_ContextCurOffset]
  52.                                             , m_StrCurOffset);
  53.                 int tmp1 = -1;

  54.                 if(m_StrCurOffset < m_SeaStrTailOffset)//m_StrCurOffset + 1 is
  55.                     //start of Good Suffix , so if m_StrCurOffset is equal to
  56.                     //m_SeaStrTailOffset, it means we can only use bad char rule

  57.                     tmp1 = (m_pWRuleProducer->m_GoodSuffixRule)[m_StrCurOffset + 1];
  58.                 int step = MAX(tmp , tmp1);
  59.                 m_ContextCurOffset = initial + step;
  60.                 m_StrCurOffset = m_SeaStrTailOffset;
  61.                 flag = false;
  62.                 break;
  63.             }
  64.         }
  65.         if(flag)
  66.         {
  67.             std::cout << (m_ContextCurOffset + 1) << std::endl; //offset begins
  68.             //with zero , bur offset in result needs to begin with 1 , so every
  69.             //offset of result add by 1

  70.             m_ContextCurOffset = (initial +
  71.                         (m_pWRuleProducer->m_GoodSuffixRule)[m_StrCurOffset]);
  72.             m_StrCurOffset = m_SeaStrTailOffset;
  73.         }

  74.         flag = true;
  75.     }
  76.     return 0;
  77. }
/*CLWRuleProducer.h*/

  1. #ifndef WORDRYLE
  2. #define WORDRYLE

  3. #include <string>
  4. #include <utility>

  5. #include <boost/unordered_map.hpp>

  6. class CLStrMatching;

  7. using boost::unordered_map;

  8. class CLWRuleProducer
  9. {

  10. public:

  11.     CLWRuleProducer(std::string word);
  12.     void init();
  13.     int pBadCharRule(char , unsigned int);
  14.     int pGoodSuffixRule();

  15.     friend class CLStrMatching;

  16. private:

  17.     std::string m_InputWord;
  18.     const int m_DefalutMoveStep;
  19.     const unsigned int m_WordLengthOffset;//search-word's length -1
  20.     const unsigned int m_GoodSuffixTail; //search-word's length -1
  21.     unordered_map<unsigned int , int> m_GoodSuffixRule;

  22.     int hasSubSuffixAhead(unsigned int);
  23.     int doHasSubSuffixAhead(unsigned int);
  24.     int isNotOnlySuffix(unsigned int);

  25. };

  26. #endif
/*CLWRuleProducer.cpp*/

  1. #include <iostream>

  2. #include "CLWRuleProducer.h"
  3. #include "PrintError.h"

  4. CLWRuleProducer::CLWRuleProducer(std::string rhs):m_InputWord(rhs) , m_DefalutMoveStep(-1)
  5.  , m_WordLengthOffset(rhs.length() - 1) , m_GoodSuffixTail(m_WordLengthOffset)
  6. {}

  7. void CLWRuleProducer::init()
  8. {
  9.     pGoodSuffixRule();
  10. }


  11. /*
  12.  * @Function Name : pBadCharRule
  13.  *
  14.  * @Description : This function use bad char and the current search-word offset
  15.  * to produce the step the search- word need to move forward and
  16.  * continue to match
  17.  *
  18.  * @para1 : the bad char in the context which don't match the search-word
  19.  *
  20.  * @para2 : the current offset of the search-word
  21.  *
  22.  * @return : the step which search-word need to move forward
  23.  *
  24.  */
  25. int CLWRuleProducer::pBadCharRule(char const badchar , unsigned int currentoffset)
  26. {
  27.     for(int ptr = currentoffset - 1 ; ptr >=0 ; --ptr)
  28.     {
  29.         if(m_InputWord[ptr] == badchar)
  30.             return (currentoffset - ptr);
  31.     }

  32.     return (currentoffset - m_DefalutMoveStep);
  33. }
  34. /*
  35.  * @Function Name : pGoodSuffixRule
  36.  *
  37.  * @Description : To produce complete Good Suffix Rule table
  38.  *
  39.  * @para : none
  40.  *
  41.  * @return : just a flag which stands for success , no meaningful
  42.  *
  43.  */
  44. int CLWRuleProducer::pGoodSuffixRule()
  45. {
  46.     int tmp = -1;
  47.     int tmp1 = -1;
  48.     for(unsigned int ptr =0 ; ptr != m_WordLengthOffset + 1; ++ptr)
  49.     {
  50.         tmp = isNotOnlySuffix(ptr);
  51.         if(tmp == -1)
  52.         {
  53.             tmp1 = hasSubSuffixAhead(ptr);

  54.             m_GoodSuffixRule[ptr] = m_GoodSuffixTail - tmp1;
  55.         }
  56.         else
  57.             m_GoodSuffixRule[ptr] = m_GoodSuffixTail - tmp;
  58.     }
  59.     return 0;
  60. }

  61. /*
  62.  * @Function Name : hasSubSuffixAhead
  63.  *
  64.  * @Description : look for if there is a sub-goodsuffix of good suffix in the
  65.  * beginning of the search-word
  66.  *
  67.  * @para1 : the beginning offset of good suffix
  68.  *
  69.  * @return : if there is , return the the sub-goodsuffix's last character's offset
  70.  * in the search-word or return -1
  71.  *
  72.  */
  73. int CLWRuleProducer::hasSubSuffixAhead(unsigned int suffixbegin)
  74. {
  75.     if(suffixbegin > m_WordLengthOffset)
  76.     {
  77.         PrintError(AT , "suffixbegin is too big in hasSubSuffixAhead function");
  78.         exit(0);
  79.     }

  80.     unsigned int ptr = m_WordLengthOffset;
  81.     int result = m_DefalutMoveStep;

  82.     while(ptr > suffixbegin)
  83.     {
  84.         result = doHasSubSuffixAhead(ptr);
  85.         if(result == -1)
  86.             --ptr;
  87.         else
  88.             break;
  89.     }
  90.     return result;
  91. }

  92. int CLWRuleProducer::doHasSubSuffixAhead(unsigned int offset)//the excute function
  93.     //for hasSubSuffixAhead function

  94. {
  95.     std::string search = m_InputWord.substr(offset , std::string::npos);
  96.     int tmp = static_cast<int>(m_InputWord.find(search));
  97.     if(tmp == 0)
  98.         return (search.length() - 1);
  99.     else
  100.         return -1;
  101. }

  102. int CLWRuleProducer::isNotOnlySuffix(unsigned int suffixbegin)
  103. {
  104.     if(suffixbegin > m_WordLengthOffset)
  105.     {
  106.         PrintError(AT , "suffixbegin is invalid");
  107.         exit(0);
  108.     }
  109.     std::string search = m_InputWord.substr(suffixbegin , std::string::npos);
  110.     int start = -1;
  111.     if(suffixbegin != 0)
  112.         start = static_cast<int>(m_InputWord.rfind(search , m_GoodSuffixTail - search.length()));
  113.     if(start == -1)
  114.         return -1;
  115.     else
  116.         return (start + search.length() - 1);
  117. }
/*PrintError.h*/

  1. #ifndef ERROR_H_INCLUDED
  2. #define ERROR_H_INCLUDED

  3. #include <stdio.h>

  4. #define STRINGIFY(x) #x
  5. #define TOSTRING(x) STRINGIFY(x)
  6. #define AT __FILE__ ":" TOSTRING(__LINE__)

  7. void PrintError(const char* , const char*);

  8. #endif
/*PrintError.cpp*/

  1. #include "PrintError.h"

  2. void PrintError(const char* location,const char* errormessage)
  3. {
  4.     fprintf(stdout,"Error at %s ; Error Discription:%s\n",location , errormessage);
  5. }
    程序已经进行了必要的注释,如有问题请留言。 
    测试如下:
(1)测试文本testfile内容:                                                
        
(2)待搜索字符串:         
        EXAMPLE 
(3)测试结果:
             
            
阅读(1684) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~