Horspool 算法
利用一些时间把近期所看所写的总结起来,对加深自己理解很有帮助,同时也可以分享交流等。
前几天研究了一下Horspool算法,感谢《柔性字符串匹配》这本书。说了这么多废话,还是回到主题。
要想深刻了解Horspool算法还需要了解BM算法,因为前者是后者的改进。BM算法是基于后缀的匹配算法。它是用移动窗口的方式来匹配的。对于BM算法,在理论上效果比Horspool好,但是实际不是如此。但是还是简单介绍一下,有助于对Horspool的理解。
BM算法思想:
对于匹配中的需要三种情况来移动窗口,需要三个函数表:
第一种:从后向前,当后缀u在模式串p中的另一位置出现。设在最右出现的位置为j(除去模式串末尾的出现)即(u=pj-u+1...pj)其中j表示尾串,这是可以将窗口(模式串的长度的大小)安全移动m-j字符(将模式串中和后缀相匹配的子串与目标串u匹配)。见《柔性字符串匹配》这本书的图,对于每个后缀都要计算它到下一个出现之间的距离(就是上面所说的m-j)设用d1(u)表示。如果模式串的所有后缀u不在p中出现,d1(u)为模式串的长度。
第二种情况:后缀u不出现在模式串中的任何位置时,这不是表示可以安全移动整个窗口位置。可能发生u自己的后缀v也有可能是模式串的一个前缀。必须处理这个情况。为了处理这个情况,需要计算模式串所有后缀的第二函数d2。同样对于模式串中的每个后缀u,d2(u)表示模式串的前缀,也表示u的后缀的最长字符串v的长度(相当u的最长后缀v(同时v也是模式串的前缀)的长度)。这种情况如下图,借鉴了书中的图:
第三种情况:因为搜索是从后向前匹配,,在文本字符 σ处不能匹配成功。则用d1移动窗口时,如果对应的模式串字符不是σ,那么就没必要进行验证匹配了,可以借助下图来理解。这样引用另一个d3函数表。用来保证下次验证时文本字符一定与模式串中一个字符σ匹配。故对于字母表(整个字母表系统)中每一个字符σ,d3(σ)表示σ在模式串中的最右出现位置到模式串末尾的距离。如果σ不出现在p中,则d3(σ)被置为m。
BM算法中的窗口移动距离还需要两次比较:
第一次:取d3(σ)和d1(u)中较大的。
第二次:取第一次的结果和m-d2(u)中较小的。
这就是移动窗口的大小。理解上面的BM思想其实还是挺麻烦的。但是有助于理解以后的字符串匹配。对于Horspool算法,其主要是摒弃了这么复杂的过程,只用d3,并且对于d3有了一些改进。使其能产生最大的跳跃。思想如下:
对于每一个搜索窗口,该算法将窗口内的文本的最后的一个字符(β)和模式串的最后一个字符比较,相等就继续搜索直到不相等(σ)的出现。然后根据β在模式串下一个出现的位置将窗口向右移动(就是d3(β))前面已经叙说了这些。用书上的图来加深理解:
理解思想后,给出书上的伪代码:
最后自己根据伪代码所写的c++源代码:
1 #include<iostream> 2 #include<map> 3 4 bool matchString(const char* vSrcStr, int vSrcStrLen, const char* vSearchStr, int vSearchStrLen, int *voPos); 5 6 7 int main(void) 8 { 9 char *SrcStr = NULL; 10 char *SearchStr = NULL; 11 12 int SrcStrLen; 13 int SearchStrLen; 14 int *Pos; 15 16 std::cout<<"input the source string length:"; 17 std::cin>>SrcStrLen; 18 if(std::cin.fail()) 19 { 20 std::cout<<"输入非法数据!"; 21 exit(0); 22 } 23 SrcStr = new char[SrcStrLen+1]; 24 std::cout<<"input the source string:"<<std::endl; 25 for(int count=0; count<SrcStrLen; count++) 26 { 27 std::cin>>SrcStr[count]; 28 if(std::cin.fail()) 29 { 30 std::cout<<"输入非法数据!"; 31 exit(0); 32 } 33 } 34 35 std::cout<<"input the target string length:"; 36 std::cin>>SearchStrLen; 37 if(std::cin.fail()) 38 { 39 std::cout<<"输入非法数据!"; 40 exit(0); 41 } 42 43 SearchStr = new char[SearchStrLen+1]; 44 std::cout<<"input the target string:"<<std::endl; 45 for(int count=0; count<SearchStrLen; count++) 46 { 47 std::cin>>SearchStr[count]; 48 if(std::cin.fail()) 49 { 50 std::cout<<"输入非法数据!"; 51 exit(0); 52 } 53 } 54 55 Pos = new int[SrcStrLen+1]; 56 matchString(SrcStr, SrcStrLen, SearchStr, SearchStrLen, Pos); 57 58 int i = 0; 59 while(i < (SrcStrLen+1) && Pos[i] != -1) 60 { 61 std::cout<<"the pos of the string are: "<<Pos[i]<<" "<<std::endl; 62 i++; 63 } 64 65 delete[] Pos; 66 delete[] SrcStr; 67 delete[] SearchStr; 68 system("pause"); 69 return 0; 70 } 71 72 73 std::map<char,int> buildStep(const char *vString, int vLength) 74 { 75 std::map<char,int> StepArray; 76 int StrPos = vLength-1; 77 for(int i=StrPos-1; i>=0; i--) 78 { 79 std::map<char,int>::iterator Iter = StepArray.find(vString[i]); 80 if(Iter == StepArray.end()) 81 { 82 StepArray.insert(std::make_pair(vString[i], (StrPos-i))); 83 } 84 } 85 return StepArray; 86 } 87 88 89 bool matchString(const char* vSrcStr, int vSrcStrLen, const char* vSearchStr, int vSearchStrLen, int *voPos) 90 { 91 for(int n=0; n<=vSrcStrLen; n++) 92 { 93 voPos[n]=-1; 94 } 95 96 std::map<char,int> moveStep = buildStep(vSearchStr, vSearchStrLen); 97 int SearchPos = 0; 98 int MatchPos; 99 100 while(SearchPos <= (vSrcStrLen-vSearchStrLen)) 101 { 102 MatchPos = vSearchStrLen-1; 103 while(MatchPos >= 0 && vSrcStr[SearchPos+MatchPos] == vSearchStr[MatchPos]) 104 MatchPos--; 105 if(MatchPos == -1) 106 { 107 int OcurrencePos = 0; 108 while(OcurrencePos < vSrcStrLen && voPos[OcurrencePos] != -1) 109 { 110 OcurrencePos++; 111 } 112 voPos[OcurrencePos] = SearchPos; 113 } 114 115 int k = 0; 116 std::map<char,int>::iterator Iter = moveStep.find(vSrcStr[SearchPos+vSearchStrLen-1]); 117 if(Iter != moveStep.end()) 118 { 119 SearchPos = SearchPos+(*Iter).second; 120 } 121 else 122 { 123 SearchPos = SearchPos+vSearchStrLen; 124 } 125 } 126 127 if(voPos[0] == -1) 128 { 129 return false; 130 } 131 132 return true; 133 }