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

最好后缀规则

    看看实际的效果

 

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

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


好后缀会出现三种情况

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


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


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

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


Only a part of the matching suffix occurs at the beginning of the pattern


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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值