字符串匹配(中)- BM算法

BM算法

  • 核心:坏字符串规则,好后缀规则

    • 主串:n
    • 模式串:m
  • 坏字符串规则

    • 模式串匹配顺序:从后往前
    • 坏字符:主串中未匹配的字符
    • si:坏字符对应模式串字符的下标
    • xi:坏字符在模式串中的下标,不存在则为-1
    • 模式串后移位数:si - xi
    • xi存在多个值时,取最大值
    • 最小时间复杂度:O(n/m),示例如下:3 - (-1)
      • 主串: 111211121112
      • 模式串:1111
    • 问题:si - xi可能是负数,示例如下:0 - 3
      • 主串:1111
      • 模式串:2111
    • 解决方案:好后缀规则
  • 好后缀规则

    • 好后缀:已经匹配的字符,记作{u}
    • 在模式串中查找另外一个和{u}匹配的子串,记作{u*}
    • 后移模式串,使{u}和{u*}对齐
    • 如果{u*}不存在,则后移,移动距离为模式串长度
    • 异常情况考虑:好后缀 子串是否和模式串前缀 子串匹配
    • 字符串s的后缀子串:最后一个字符和s对齐的 子串,示例如下
      • s:123
      • s后缀子串:23,3
    • 前缀子串:类似后缀子串
  • 规则选取

    • 分别计算坏字符串和好后缀的值,取大值当做后移的值
    • 坏字符xi查找优化:散列表
  • 实现

//主串a, 模式串b
function bm(a,b){
	let al = a.length
	let bl = b.length
	let dict = {}
	for(let i=0;i<bl;i++){
		dict[b[i]] = i		//相同值取大值
	}
	let suffix = []
	let prefix = []
	generateGS(b, suffix, prefix)
	let i = 0
	while(i <= al-bl){
		let j
		for(j = bl-1; j >= 0; j--){
			if(a[i+j] != b[j]) break
		}
		if(j < 0) return i
		let x = j - getXi(a[i+j])
		let y = 0
		if(j < bl - 1){	//有好后缀
			y = moveByGS(j)
		}
		i += Math.max(x,y)
	}
	return -1
	function getXi(v){
		return dict[v] !== undefined ? dict[v] : -1
	}
	//j表示坏字符串对应的模式串中字符下标
	function moveByGS(j){
		let k = bl - 1 - j	//好后缀长度
		if(suffix[k] != -1) return j - suffix[k] + 1	//匹配好后缀的子串
		//遍历好后缀子串:从第二个字符(j+2)到 最后(b1)
		for(let r = j + 2; r < bl ; r++){				
			//后缀子串有对应的前缀
			if(prefix[bl-r]){
				return r
			}
		}
		return bl
	}
}

function generateGS(b,suffix,prefix){
	let m = b.length
	//初始化suffix,prefix
	for(let i=0; i < m; i++){
		suffix[i] = -1
		prefix[i] = false
	}
	//遍历b,取每项值b[j],与最后一项b[m-1]比较
	for(let i = 0; i < m-1; i++){
		let j = i	//后缀子串匹配的子串的起始坐标
		let k = 0	//公共后缀子串长度
		//当前项和最后一项相等后,继续往前比较
		while(j >= 0 && b[j] == b[m-1-k]){	
			j--		//往前匹配
			k++		//往前
			suffix[k] = j + 1	//
		}
		if(j == -1) prefix[k] = true	//后缀子串匹配到了前缀子串
	}
}

//测试
bm('cabcabd','cabd')		//3
bm('acabcbcbacabc','cbacabc')	//6
bm('2111','1111')			//-1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值