001字符串BM匹配算法

前言:

1、BM匹配算法适用于,在一个字符串originStr中,搜索targetStr是否存在于originStr中。其中,bm无特殊含义,在话术上,称originStr为主串,targetStr为模式串;

2、在主串中寻找模式串,最简单的方法莫过于暴力搜索。即拿模式串的每一位与主串相比,如果模式串的每一位都在主串中匹配上,则认为找到了。否则,模式串向后滑动一位,紧接着如上操作。直到找到,或找不到。该方法称为BF;

3、BF的最大时间复杂度为O(n*m),其中n为主串长度,m为模式串长度。乍一看,BF并不高效,但在主串长度小,模式串长度小的情况下,特别是模式串长度小的情况下,是非常适用的;

4、BF稍微改造下,可变成RK,RK算法没有从本质上改善BF的时间复杂度。它利用哈希技巧,将模式串的每位比较,改变为一次模式串哈希值比较。但主串的子串哈希值,以及模式串的哈希值,总还是需要根据串中的每一位字符计算得出;

5、BM算法与RK、BF有本质区别,BM算法根据已比较过的子串,来得出模式串应向后滑动多少位,而不是像RK、BF仅向后固定滑动一位;

6、BM算法由坏字符规则、好后缀规则组成,模式串向后滑动的位数取这两个规则下的最大滑动位数;

一、BM算法的匹配方向;

BM匹配时,会拿最后一位字符作为匹配的开始,其匹配方向为向前匹配。

举例:abc与dbc的匹配顺序如下:

1)拿abc的c,与dbc的c比较;

2)拿abc的b,与dbc的b比较;

3)拿abc的a,与dbc的d比较。

二、坏字符规则;

假设主串:abcacabdc

模式串为:abd

1)第一次比较时,主串孩子则为abc;

2)第一个匹配,为c与d的匹配;

3)c与d不相等,c被称为坏字符(坏字符为主串上不与模式串相等的字符)。

4)如果坏字符c,在模式串中不存在,那意味着坏字符无法在模式串的任意一个字符上匹配上。这个时候,可以将模式串向后移动与模式串长度的位数。

5)如果坏字符c,在模式串中存在,那意味着可以将模式串的c与坏字符的c对齐。

因为如果模式串的下一次移动刚好能匹配上主串的子串的话,那么模式串的c与坏字符的c就肯定是对齐的。

需要特别注意的是,假如模式串中存在多个字符与坏字符相等,为避免模式串过度滑动,那么应该是模式串的最后一个c与坏字符对齐。因为模式串最后一个c,与坏字符的距离总是最近的。

 

三、坏字符规则实现代码;

/**
	 * 获取坏字符规则下的位移
	 * @param badChar
	 * @param target
	 * @param badIndex
	 * @return
	 */
	private static int getBadCharShift(String badChar, String target, int badIndex) {
		boolean isBadCharExistInTarget = false;
		int lastMatch = -1;
		for(int i = 0; (i+1) <= target.length(); i++) {
			String s = target.substring(i, i+1);
			if(badChar.equals(s)) {
				isBadCharExistInTarget = true;
				lastMatch = i;
			}
		}
		
		if(isBadCharExistInTarget) {
			return badIndex-lastMatch;
		}
		return target.length();
	}

待优化点:模式串中向前查找坏字符更为合理。

四、好后缀规则;

假设主串:a a a b a b

模式串为:a b a b

1)匹配位置如下时:

2)孩子主串aaab与模式串abab比较时,后缀有ab是相同的,这样的后缀称为好后缀;具体如下图:

3)如果模式串中存在另外一个与好后缀相同的子串,这意味着重新移动模式串,使该子串与好后缀对齐,就有可能在主串上得到完全与模式串匹配的子串;举例如下:

假设主串、模式串、好后缀如图001;

这个时候,在模式串中可以找到另一个与好后缀相同的子串,将模式串向后移动两位,模式串刚好就与主串的某个子串完全匹配上,见图002;

4)如果在模式串中没有找到另外一个子串与好后缀完全相同的子串,这个时候,并不能武断地将模式串过度地滑动越过好后缀。因为模式串中有可能存在子串与好后缀的后缀子串相同,后缀子串与好后缀的效果是完全一样的。具体如下图:

a a a b a b b a b

b b a b

好后缀为ab,在模式串bbab中并没有找到另外一个好后缀ab,如图003:

但是模式串存在好后缀的后缀子串b,将模式串移动两位,使模式串上,最靠近好后缀子串的好后缀子串,对准好后缀子串。恰好就在主串上得到了一个完全与模式串匹配的子串,如图004:

5)好后缀子串的定义举例如下,比如好后缀abcd,那么其好后缀子串如下:

abcd、bcd、cd、d

五、好后缀规则代码;

1)获取好后缀规则下的位移;

/**
	 * 获取好后缀规则下的位移
	 * @param target
	 * @param goodTail
	 * @param badIndex
	 * @return
	 */
	private static int getGoodTailShift(String target, String goodTail, int badIndex) {
		if(goodTail == null || goodTail.length() < 1) {
			return -1;
		}
		return otherGoodTailLastInTarget(target, goodTail, badIndex);
	}

2)获取模式串中最靠近好后缀的另一个好后缀位置;

/**
	 * 获取模式串中最靠近好后缀的另一个好后缀位置
	 * @param target
	 * @param goodTail
	 * @param badIndex
	 * @return
	 */
	private static int otherGoodTailLastInTarget(String target, String goodTail, int badIndex) {
		int index = -1;
		List<String> goodTailChildList = goodTailChildList(goodTail);
		for(int i = 0; i < goodTailChildList.size(); i++) {
			String goodTailChild = goodTailChildList.get(i);
			index = goodTailChildLastInTarget(target, goodTailChild, badIndex);
			if(index >= 0) {
				index = target.length() - index - 1;
				break;
			}
		}
		return index;
	}

3)获取好后缀的所有好后缀孩子串;

/**
	 * 获取好后缀的所有好后缀孩子串,时间复杂度为O(n),n为goodTail的长度
	 * 举例:ABCD的所有好后缀孩子串为ABCD、BCD、CD、D
	 * @param goodTail
	 * @return
	 */
	private static List<String> goodTailChildList(String goodTail){
		List<String> result = new ArrayList<String>();
		for(int i = 0; i < goodTail.length(); i++) {
			String s = goodTail.substring(i, goodTail.length());
			result.add(s);
		}
		return result;
	}

4)获取模式串中最靠近指定好后缀孩子的另一个好后缀孩子位置;

/**
	 * 获取模式串中最靠近指定好后缀孩子的另一个好后缀孩子位置,时间复杂度O(n*m),n为target长度,m为goodTail长度
	 * @param target
	 * @param goodTail
	 * @param badIndex
	 * @return
	 */
	private static int goodTailChildLastInTarget(String target, String goodTail, int badIndex) {
		int goodTailLen = goodTail.length();
		for(int i = (target.length()-1); (i-goodTailLen) >= 0; i--) {
			int startIndex = i-goodTailLen;
			String maybeGoodTail = target.substring(startIndex, i);
			if(isEqual(maybeGoodTail, goodTail)) {
				return startIndex;
			}
		}
		return -1;
	}

五、其它;

1、代码地址:https://github.com/haishui211/dataStructureRep.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值