字符串匹配——《算法导论》第32章


部分示意图来源于《算法导论》

1. 朴素字符串匹配算法

思路:模板沿着文本滑动,对每个偏移都进行检测
最坏情况下,算法运行时间为 O ( ( n − m + 1 ) m ) O((n-m+1)m) O((nm+1)m)

2. Rabin-Karp算法

该算法是基于散列的,用的是哈希表。
模板:P[1…m]
文本:T[1…n]
对T的所有m长度的子字符串计算d^{m-1}(利用霍纳法则可以简化),通过取模,减少哈希值的长度。
哈希值 t s = d m − 1 t_s= d^{m-1} ts=dm1 mod q,q为一个素数,且需满足dq在一个计算机字长内(比如十进制时,d=10,10q需要比 ?小,因此q的值选为13。)
通过比较n-m+1次哈希值: t s = p t_s= p ts=p mod q,再用朴素法来检测有效偏移s是真的有效还是伪命中点。
图例:
在这里插入图片描述

算法性能分析:
预处理时间为 θ ( m ) \theta(m) θ(m),最坏情况下,匹配时间是 θ ( ( n − m + 1 ) ∗ m ) \theta((n-m+1)*m) θ((nm+1)m),选取的q大于模式p的长度时,匹配时间为 O ( n + m ) O(n+m) O(n+m),由于m≤n,算法的平均匹配时间为 O ( n ) O(n) O(n)

3. KMP算法

用的是前缀表,使用前缀表可以告诉我们匹配失败之后跳到哪里重新匹配。
模式P的前缀函数 π \pi π包含模式与自身偏移进行匹配的信息。
图例:
请添加图片描述
步骤:

  1. 利用双指针求出模式p的前缀表next
  2. 对原字符串T进行匹配

go 代码

go语言实现kmp算法

package main

import "fmt"

func strStr(t string, p string) int {
    if len(p)==0{
        return 0
    }
	next := compute_next(p)
	j := 0
	for i := 0; i < len(t); i++ {
		for t[i] != p[j] { //边界j=0时的处理
			if j == 0 {
				break
			}
			j = next[j-1]

		}
		if t[i] == p[j] {
			j++
		}
		if j == len(p) {
			return i-len(p)+1
		}
	}

	return -1
}

func compute_next(p string) []int {
	m := len(p)
	next := make([]int, m) //next[0]=0
	for left, right := 0, 1; right < m; right++ {
		for p[left] != p[right] {
			//边界left=0时的处理
			if left == 0 {
				break
			}
			left = next[left-1]
		}
		if p[left] == p[right] {
			left++
		}
		next[right] = left

	}
	return next
}

func main() {
	haystack := "aaaaa"
	needle := "bba"
	next := strstr(haystack, needle)
	fmt.Println(next)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值