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
最好后缀规则
看看实际的效果
上面倒数第二步匹配是没必要的。为什么呢?在倒数第三步匹配过程中,已有最后两个字符与模式串P中匹配,而模式串中有前两个与后两个字符相同的,所以可以直接在接下来将P中的前两个与主串中匹配过的’ab’对齐,做为下一次匹配的开始。
求坏字符非常容易就不说了,好后缀就有点麻烦了
好后缀会出现三种情况
第一种:可以找到相同的子串和已成功匹配的串相同
| |
Figure 1: The matching suffix (gray)occurs somewhere else in the pattern | |
第二种:模式串的前缀后已成功匹配的串部分相同
| |
Figure 2: Only a part of the matching suffix occurs at the beginning of the pattern |
/*************************************
* 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;
}