KMP算法和BM算法

KMP算法利用模式串本身包含的信息来增加移动的步伐,KMP算法的核心是next数组,next [ i ] = j 表示当第 i 位匹配失败时,应该从模式串第 j 位开始重新匹配

example:             0 1 2 3 4 5 6 7 8 9

                       T : a b a a b a b c a b

                       P : a b a b c a b

next[3]=1                    a b a b c a b

在第三位匹配失败,那么此时应该把P向右移动若干为重新匹配,那么移动到哪里呢,这就是next的值了

next[i] 定义为最大的 j 使得 P[ 1…..j-1 ] == P[ i-j+1……i-1 ]

求next数组

//KMP算法求next数组 void Next(char *str) { int n=strlen(str); int i=0,j=-1; next[0]=-1; while(i<n) { while(j!=-1&&str[i]!=str[j]) j=next[j]; i++;j++; next[i]=j; } }

匹配

int match(char *text,char *pattern) { Next(pattern); int n=strlen(text),m=strlen(pattern); int i=0;j=0; while(i<n-m+1&&j<m) { if(j==-1 || text[i]==pattern[j]) { i++;j++; } else j=next[j]; } if(j>=m) return i-m; return -1; }


BM算法

BM算法利用坏字符和好后缀来最大化移动距离,至于什么是坏字符和好后缀的话见http://www.inf.fh-flensburg.de/lang/algorithmen/pattern/bmen.htm

最好后缀规则

    看看实际的效果

  《KMP算法和BM算法》

《KMP算法和BM算法》

       上面倒数第二步匹配是没必要的。为什么呢?在倒数第三步匹配过程中,已有最后两个字符与模式串P中匹配,而模式串中有前两个与后两个字符相同的,所以可以直接在接下来将P中的前两个与主串中匹配过的’ab’对齐,做为下一次匹配的开始。

求坏字符非常容易就不说了,好后缀就有点麻烦了


好后缀会出现三种情况

第一种:可以找到相同的子串和已成功匹配的串相同


《KMP算法和BM算法》


Figure 1:  The matching suffix (gray)occurs somewhere else in the pattern

 第二种:模式串的前缀后已成功匹配的串部分相同


《KMP算法和BM算法》


Figure 2:  Only a part of the matching suffix occurs at the beginning of the pattern

第三种:前两种情况都不存在,那么可以直接向右移动strlen的长度


/************************************* * 0 1 2 3 4 5 6 7 8 * a b b a b a b * f[i]是满足表达式 str[i---i+n-j-1]==str[j---n-1]的最小的j,即是求suffix[i]的前缀和模式串的后缀最长的公共部分的起点 * s[i]是当第i-1位不匹配时模式串可以移动的大小 * 求f[i]借助了KMP中求next数组的想法,不过和求next不同的是,这里f[i]是包括i的,而next是不包括i的,next[i]是求前i-1位的前缀和后缀的最长公共部分,而f[i]是求后n-i+1位的前缀和后缀的最长公共部分 */ //BM算法求好后缀(第一种情况) void goodSuffix1(char *str) { int i,j; int n=strlen(str); i=n;j=n+1; f[n]=n+1; while(i>0) { while(j<=n && str[i-1]!=str[j-1]) { //走到这部说明了前面f[i]=j了,而且时最小的j满足f[i]的条件的,此时str[i-1]!=str[j-1],就可以计算出当第j-1位匹配失败时 //应该对应到哪位重新匹配,比如假如现在在文本串中t和j-1匹配失败,那么现在就应该调整让t和i重新匹配,也就是向右移动j-i位, //所以s[j]=j-i; if(s[j]==0) s[j]=j-i; j=f[j]; } i--;j--; f[i]=j; } } //好后缀(第二种情况) void goodSuffix2(int n) //n为字符串长度 { int i,j=f[0]; for(i=0;i<=n;i++) { if(s[i]==0) s[i]=j; if(i==j) j=f[j]; } } void goodSuffix(char *pattern) {     int n=strlen(pattern);     goodSuffix1(pattern);     goodSuffix2(n); } 


匹配:

int BMmatch(char *text,char *pattern) { badcharacter(pattern); goodSuffix(pattern); int n=strlen(text),m=strlen(pattern); int i=0,j; while(i<=n-m) { j=m-1; while(j>=0&&text[i+j]==pattern[j]) j--; if(j<0) return i; i+=max(s[j+1],j-badshift[text[i+j]]); } return -1; }



点赞