单模式串匹配算法、多模式匹配算法

strings.Index(text, pattern)

BF算法

BF=Brute Force,暴力匹配算法,最坏情况时间复杂度为O(n*m),实际上是在主串中检查起始位置分别是0、1、2...n-m且长度为m的n-m+1个子串,查看有无与模式匹配的

func BruteForceSearch(text string,pattern string) int {
	j := 0
	n := len(text)
	m := len(pattern)
	for i:=0;i<n-m;i++ {
		j = 0;
		for j < m && pattern[j] == text[i+j] {
			j++
		}
		if j == m {
			return i
		}
	}
	return -1
}

RK算法

RK=Rabin-Karp,在BF的基础上引入哈希算法,对主串的n-m+1个子串分别求哈希值,然后逐个与模式串比较哈希值,如果相等则匹配,设要匹配的字符串的字符集中包含K个字符,用一个K进制数表示一个子串,将该K进制数转化为十进制数作为子串哈希值

func RobinKarp(text string, pattern string) int {
	n := len(text)
	m := len(pattern)
	prime := 101
	powm := 1
	TextHash := 0
	PatternHash := 0
	i, j := 0, 0
	if m == 0 || m > n {
		return -1
	}
	for i = 0; i < m-1; i++ {
		powm = (powm << 1) % prime
	}
	for i = 0; i < m; i++ {
		PatternHash = ((PatternHash << 1) + int(pattern[i])) % prime
		TextHash = ((TextHash << 1) + int(text[i])) % prime
	}
	for i = 0; i < (n - m); i++ {
		if TextHash == PatternHash {
			for j = 0; j < m; j++ {
				if text[i+j] != pattern[j] {
					break
				}
			}
			if j == m {
				return i
			}
		}
		TextHash = (((TextHash - int(text[i])*powm) << 1) + int(text[i+m])) % prime
		if TextHash < 0 {
			TextHash += prime
		}
	}
	return -1
}

BM算法

包含两部分:坏字符规则(bad character rule) & 好后缀规则(good suffix shift)

坏字符规则

从模式串的末尾倒序匹配,发现某字符未匹配则称其为坏字符,将该坏字符c在模式串中寻找,发现模式串中不存在该字符,则将模式串全部滑动到c后面的位置,当发生不匹配时,将坏字符对应的模式串中的字符下标记为si,若坏字符在模式串中存在,则记录在模式串中的下标记为xi,不存在则为-1,移动位数=si-xi

好后缀规则

将已经匹配的字符串称为好后缀,记为{u},将其在模式串中查找,找到另一个与{u}相匹配的子串{u'},将模式串滑动到子串{u'}与主串中{u}对齐的位置

KMP算法

将好前缀本身,在其后缀子串中,查找最长的可以和好前缀的前缀子串匹配的,假设最长的可匹配的那部分前缀子串是{v},长度为k,将模式串一次性往后滑动j-k位,相当于每次遇到坏字符时,就把j更新位k,i不变,继续比较

将好前缀的所有后缀子串中最长的可匹配前缀子串的后缀子串叫作最长可匹配后缀子串,对应的前缀子串叫作最长可匹配前缀子串

构建一个数组,用于存储模式串中每个前缀的最长可匹配前缀子串的结尾字符下标,将其定义为next数组

func KMPPreprocess(pattern string, next []int) {
	m := len(pattern)
	i := 0
	j := -1
	next[i] = -1
	for i < m {
		for j >= 0 && pattern[i] != pattern[j] {
			j = next[j]
		}
		i++
		j++
		next[i] = j
	}
}

func KMP(text string, pattern string) int {
	i, j := 0, 0
	n := len(text)
	m := len(pattern)
	next := make([]int, m+1)
	KMPPreprocess(pattern, next)
	for i < n {
		for j >= 0 && text[i] != pattern[j] {
			j = next[j]
		}
		i++
		j++
		if j == m {
			return i - m
		}
	}
	return -1
}