字符串匹配(上)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_1290259791/article/details/88171759

字符串匹配(上)

  • BF算法:一种暴力匹配的算法。
  • RK算法:基于BF算法的改进,利用了哈希算法。
  • BM算法:
  • KMP算法:

这四种都是单模式匹配的算法,就是一个字符串跟另一个字符串匹配。

BF算法

BF: Brute Force的缩写

中文名:暴力匹配算法、朴素匹配算法

时间复杂度:O(n*m)

示例: 在字符串A中查找字符串B,那么字符串A就是 主串 ,字符串B就是 模式串 ,主串长度记作n,模式串长度记作m。

  • BF算法: 在主串中,检查起始位置分别是0、1、2…n-m且长度为m的 n-m+1 个子串,看有没有跟模式串匹配的。
  • 时间复杂度: 每次对比m个字符串,要对比 n-m+1 次,最坏时间复杂度是 O(n*m)
  • 优点: 虽然 BF 算法时间复杂度高,但是实际开发中,却是常用的字符串匹配算法。
    • 实际开发中,模式串和主串长度不会太长,并且每次模式串与主串的子串匹配时,途中遇到不能匹配的字符时,就可以停止,不需要完全对比。虽然理论时间复杂度 O(n*m) 大部分情况效率高很多。
    • 算法简单,代码实现非常简单,在满足性能的前提下,简单是首选。
def bf(main, pattern):
	"""
	字符串匹配,bf暴力搜索
	main:主串
	pattern:模式串
	"""
	n, m = len(main), len(pattern)
	if n <= m:
		return 0 if pattern == main else -1

	for i in range(n - m + 1):
		for j in range(m):
			if main[i + j] == pattern[j]:
				if j == m - 1:
					return i
				else:
					continue
			else:
				break
	return -1

if __name__ == '__main__':
	res = bf('i like apple', 'ke')
	print(res)

RK算法

RK:Rabin-Karp算法

BF算法的升级版,利用了哈希算法,减少了模式串和子串的比较时间。

时间复杂度 理想:O(n) 最差:O(n*m)

在BF算法时,我们需要暴力的对比 n-m+1 个子串与模式串,找出主串与模式串匹配的子串。

每次检查主串与子串是否匹配,需要依次对比每个字符,BF算法的时间复杂度比较高是 O(n*M)

优化: 我们队朴素字符串匹配算法改进,引入哈希算法。

RK算法: 通过哈希算法对主串的 n-m+1 个字串求哈希值,然后逐个与模式串的哈希值比较大小。若某个子串的哈希值与模式串相等,就说明匹配成功(不考虑哈希冲突问题),数字之间的比较非常快,所以模式串和子串比较的效率就提高了。

缺点: 计算哈希值需要遍历子串中的每个字符,尽管模式串与子串比较效率提高了,算法的整体效率没提高。

优化: 比如要处理的字符串只包含 a~z 这 26 个小写字母,那我们就用26进制来表示一个字符串。

时间复杂度: 只需要扫描一遍主串计算出所有子串的哈希值,这部分时间复杂度 O(n),模式串和每个子串哈希值比较时间复杂度 O(1),总共比较 n-m+1 个子串的哈希值,这部分时间复杂度 O(n),RK算法的整体时间复杂度 O(n)

问题: 当模式串很长,对应的子串也会很长,得到的哈希值就会很大。刚刚的优化方法是没有散列冲突的,就是一个字符串与一个26进制数一一对应。当我们按a~z对应的26个数字来进行相加,产生的哈希冲突会很高。

解决问题: 我们可以判断两个哈希值如果相同,我们再去对比下子串和模式串本身。当存在大量冲突会退化成 O(n*m) ,一般情况下 RK 比 BF 算法高。

def simple_hash(s, start, end):
	"""
	计算子串哈希值
	每个字符取acs-ii码后求和
	"""
	assert start <= end
	ret = 0
	for c in s[start:end+1]:
		ret += ord(c)
	return ret

def rk(main, pattern):
	"""
	rk匹配
	main:主串
	pattern:模式串
	"""
	n, m = len(main), len(pattern)
	if n <= m:
		return 0 if pattern == main else -1

	# 子串哈希值表
	hash_memo = [None] * (n-m+1)
	hash_memo[0] = simple_hash(main, 0, m-1)
	for i in range(1, n-m+1):
		hash_memo[i] = hash_memo[i - 1] - simple_hash(main, i-1, i-1) + simple_hash(main, i+m-1, i+m-1)

	# 模式串哈希值
	hash_p = simple_hash(pattern, 0, m-1)

	for i, h in enumerate(hash_memo):
		if h == hash_p:
			if pattern == main[i:i+m]:
				return i
			else:
				continue
	return -1

if __name__ == '__main__':
	res = rk('i like apple', 'ke')
	print(res)

猜你喜欢

转载自blog.csdn.net/qq_1290259791/article/details/88171759