github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/bm/bm.go (about)

     1  package bm
     2  
     3  /*
     4  bm 字符匹配算法 Boyer-Moore
     5  1. 好串规则 bad character rule 好串表示已匹配字符
     6  2. 坏串规则 good suffix shift  坏串表示不匹配字符
     7  */
     8  
     9  // 构建 k:字符 v:位置 的hash表
    10  func generateBc(b []rune) map[rune]int {
    11  	ret := make(map[rune]int, len(b))
    12  	for index, v := range b {
    13  		ret[v] = index
    14  	}
    15  	return ret
    16  }
    17  
    18  // 返回第一个匹配的位置
    19  func BmSearch(mainStr, pattern []rune) int {
    20  	if len(mainStr) == 0 || len(pattern) == 0 || len(pattern) > len(mainStr) {
    21  		return -1
    22  	}
    23  	//构建坏字符哈希表
    24  	bc := generateBc(pattern)
    25  	n := len(mainStr)
    26  	m := len(pattern)
    27  	suffix, prefix := generateGS(pattern)
    28  	step := 1
    29  	// 主串与模式对齐的第一个字符的位置
    30  	for i := 0; i <= n-m; i += step {
    31  
    32  		//查找坏字符串的位置
    33  		badCharIndex := m - 1                     // badCharIndex 表示坏字符在模式串的位置
    34  		for ; badCharIndex >= 0; badCharIndex-- { // 从后往前进行匹配
    35  			if mainStr[i+badCharIndex] != pattern[badCharIndex] {
    36  				break //找到了坏字符的位置
    37  			}
    38  		}
    39  
    40  		if badCharIndex <= 0 {
    41  			// 没有坏串就匹配成功,返回主串与模式串第一个匹配的字符的位置
    42  			return i
    43  			/*
    44  				// 如果此处如果要继续匹配
    45  				step =1
    46  				continue
    47  			*/
    48  		}
    49  		/*
    50  			1. 坏串规则:
    51  			 i+badCharIndex是坏字符在主串的位置, mainStr[i+badCharIndex]就是坏串的字符
    52  			 bc[mainStr[i+badCharIndex]]就是坏串字符在模式串的位置 没有就是 -1
    53  			 badCharIndex-bc[mainStr[i+badCharIndex]] 就是要向后滑动的距离
    54  		*/
    55  		bcIndex := -1
    56  		if i, ok := bc[mainStr[i+badCharIndex]]; ok {
    57  			bcIndex = i
    58  		}
    59  		stepForBC := badCharIndex - bcIndex // 坏串滑动位数
    60  
    61  		/*
    62  			2. 好串规则
    63  		*/
    64  		stepForGS := -1
    65  		if badCharIndex < m-1 { //如果有好串后缀
    66  			stepForGS = moveByGS(badCharIndex, m, suffix, prefix) // 计算位移
    67  		}
    68  		// 向后移动位置
    69  		step = max(stepForBC, stepForGS)
    70  		if step <= 0 { // 防止负数
    71  			step = 1
    72  		}
    73  	}
    74  	return -1
    75  }
    76  
    77  func max(a, b int) int {
    78  	if a > b {
    79  		return a
    80  	}
    81  	return b
    82  
    83  }
    84  
    85  // badChartIndex 表示坏字符在模式串中的位置 ; m 表示模式串长度
    86  func moveByGS(badCharIndex, patternLen int, suffix []int, prefix []bool) int {
    87  	k := patternLen - 1 - badCharIndex // k 已经匹配后缀的长度
    88  	if suffix[k] != -1 {               //complete match 直接找到位置了
    89  		return badCharIndex - suffix[k] + 1
    90  	}
    91  
    92  	// 否则匹配后缀子串
    93  	for r := badCharIndex + 2; r < patternLen-1; r++ {
    94  		//
    95  		if prefix[patternLen-r] { // patternLen-r 表示后缀子串的长度
    96  			return r
    97  		}
    98  	}
    99  	//no match
   100  	return patternLen
   101  }
   102  
   103  // 返回好串 后缀表 与 前缀表
   104  func generateGS(b []rune) ([]int, []bool) {
   105  	m := len(b)
   106  	// 存储的是该位置的字符,当前位置倒数上一次出现的位置的下标
   107  	// cabcab 最后一个b index=5,只有1个字符 ,上一次出现的b index=2,
   108  	// 所以 suffix[1]=2
   109  	suffix := make([]int, m)
   110  	// 后缀子串与前缀子串的位置是否匹配
   111  	prefix := make([]bool, m)
   112  	for i := 0; i < m; i++ {
   113  		suffix[i] = -1
   114  		prefix[i] = false
   115  	}
   116  	for i := 0; i < m-1; i++ { //b[0;i]
   117  		// 此处从后往前数的指针
   118  		j := i // 从 j 位置往前数
   119  		k := 0
   120  		for j >= 0 && b[j] == b[m-1-k] { // 与 b[0, m-1] 求公共后缀子串
   121  			// m-1-k 理解为从末尾往前数
   122  			j--
   123  			k++
   124  			// k 表示长度
   125  			suffix[k] = j + 1 //j+1 表示公共后缀子串在 b[0, i] 中的起始下标
   126  		}
   127  		if j == -1 {
   128  			prefix[k] = true // 如果公共后缀子串也是模式串的前缀子串
   129  		}
   130  	}
   131  	return suffix, prefix
   132  }