首页 > 其他分享 >2023-11-11:用go语言,字符串哈希+二分的例题。 给定长为 n 的源串 s,以及长度为 m 的模式串 p, 要求查找源串中有多少子串与模式串匹配, s‘ 与 s 匹配,当且仅当 s‘ 与 s

2023-11-11:用go语言,字符串哈希+二分的例题。 给定长为 n 的源串 s,以及长度为 m 的模式串 p, 要求查找源串中有多少子串与模式串匹配, s‘ 与 s 匹配,当且仅当 s‘ 与 s

时间:2023-11-11 20:24:51浏览次数:37  
标签:11 源串 匹配 复杂度 模式 算法 哈希 子串

2023-11-11:用go语言,字符串哈希+二分的例题。

给定长为 n 的源串 s,以及长度为 m 的模式串 p,

要求查找源串中有多少子串与模式串匹配,

s' 与 s 匹配,当且仅当 s' 与 s 长度相同,且最多有 k 个位置字符不同。

其中 1 <= n, m <= 10^6,0 <= k <= 5。

来自左程云

答案2023-11-11:

go代码用灵捷3.5编写。

大体过程如下:

算法1:

遍历源串 s 中所有长度为 m 的子串,判断子串与模式串的不同字符数量是否小于等于 k,若是,将统计的子串数量加1。具体地:

1.首先计算源串 s 的长度 n 和模式串 p 的长度 m。

2.若 n < m,则返回0。

3.将源串 s 和模式串 p 转换为 rune 类型的切片,方便进行字符比较。

4.遍历源串 s 中所有长度为 m 的子串,判断子串与模式串的不同字符数量是否小于等于 k,若是,将统计的子串数量加1。

5.返回统计的子串数量。

算法2:

计算源串 s 的哈希值和模式串 p 的哈希值,然后遍历源串 s 中所有长度为 m 的子串,判断子串与模式串的哈希值是否相等,若是则比较子串与模式串的每个字符是否相同,最多允许 k 个字符不同。具体地:

1.首先计算源串 s 的长度 n 和模式串 p 的长度 m。

2.若 n < m,则返回0。

3.将源串 s 和模式串 p 转换为 rune 类型的切片,方便进行哈希操作。

4.计算源串 s 的哈希值和模式串 p 的哈希值,分别保存在 hashs 和 hashp 数组中。

5.遍历源串 s 中所有长度为 m 的子串,判断子串与模式串的哈希值是否相等,若是则比较子串与模式串的每个字符是否相同,最多允许 k 个字符不同。

6.比较子串与模式串的每个字符是否相同,最多允许 k 个字符不同的具体实现:遍历子串中每个字符,二分查找在模式串中与该字符相同的位置,若找到了,则比较子串和模式串中该位置的字符是否相同,否则允许 k 的值加1。如果 k 的值大于了允许的最大值,那么返回 false。

7.返回 true。

时间复杂度和空间复杂度分析:

算法1:

时间复杂度:代码中主要的时间复杂度来源于遍历源串 s 中所有长度为 m 的子串,遍历次数为 O(n-m+1),每次遍历需要比较 m 个字符,因此总的时间复杂度为 O((n-m+1)*m)。由于 n 和 m 的值均不超过 10^6,因此算法1的总的时间复杂度为 O(10^12),在实际情况中运算速度较慢。

空间复杂度:算法1只需要占用 O(n+m) 的额外空间,用于存储源串 s 和模式串 p。

算法2:

时间复杂度:代码中主要的时间复杂度来源于计算源串 s 和模式串 p 的哈希值,以及遍历源串 s 中所有长度为 m 的子串,遍历次数为 O(n-m+1),每次需要计算哈希值和比较 m 个字符,因此总的时间复杂度为 O((n-m+1)*(m+logn))。由于 n 和 m 的值均不超过 10^6,因此算法2的总的时间复杂度为 O(10^12),与算法1的时间复杂度相同,但实际速度更快。

空间复杂度:算法2需要占用 O(n+m) 的额外空间,用于存储源串 s 和模式串 p 的哈希值,以及 O(n) 的额外空间,用于存储哈希值计算过程中的幂 base^i(i<=n)。

总结:

算法1和算法2的时间复杂度都为 O(10^12),但实际运算速度可能存在很大的差异,具体取决于实现过程中的细节。在实际应用中,算法2比算法1更为常用,因为哈希算法能够在较快的时间内完成字符串的比较。

go完整代码如下:

package main

import (
	"fmt"
	"math/rand"
)

func howMany1(str1 string, str2 string, k int) int {
	n := len(str1)
	m := len(str2)
	if n < m {
		return 0
	}
	s := []rune(str1)
	p := []rune(str2)
	ans := 0
	for i := 0; i <= n-m; i++ {
		if diffLessK1(s, i, p, k, m) {
			ans++
		}
	}
	return ans
}

func diffLessK1(s []rune, i int, p []rune, k int, m int) bool {
	diff := 0
	for j := 0; j < m; j++ {
		if s[i+j] != p[j] {
			diff++
		}
	}
	return diff <= k
}

const MAXN = 100001
const base = 1000000007

var pow []int64 = make([]int64, MAXN)
var hashs []int64 = make([]int64, MAXN)
var hashp []int64 = make([]int64, MAXN)

func buildHash(s []rune, n int, p []rune, m int) {
	hashs[0] = int64(s[0] - 'a' + 1)
	for j := 1; j < n; j++ {
		hashs[j] = hashs[j-1]*base + int64(s[j]-'a'+1)
	}
	hashp[0] = int64(p[0] - 'a' + 1)
	for j := 1; j < m; j++ {
		hashp[j] = hashp[j-1]*base + int64(p[j]-'a'+1)
	}
}

func howMany2(str1 string, str2 string, k int) int {
	n := len(str1)
	m := len(str2)
	if n < m {
		return 0
	}
	s := []rune(str1)
	p := []rune(str2)
	buildHash(s, n, p, m)
	ans := 0
	for i := 0; i <= n-m; i++ {
		if diffLessK2(i, i+m-1, 0, m-1, k) {
			ans++
		}
	}
	return ans
}

func diffLessK2(l1 int, r1 int, l2 int, r2 int, k int) bool {
	diff := 0
	for l1 <= r1 && diff <= k {
		l, r := 1, r1-l1+1
		len := 0
		for l <= r {
			m := (l + r) / 2
			if ok(l1, l2, m) {
				len = m
				l = m + 1
			} else {
				r = m - 1
			}
		}
		if l1+len <= r1 {
			diff++
		}
		l1 += len + 1
		l2 += len + 1
	}
	return diff <= k
}

func ok(l1 int, l2 int, len int) bool {
	return hash(hashs, l1, l1+len-1) == hash(hashp, l2, l2+len-1)
}

func hash(hash []int64, l int, r int) int64 {
	ans := hash[r]
	if l == 0 {
		return ans
	}
	ans -= hash[l-1] * pow[r-l+1]
	return ans
}

func init() {
	pow[0] = 1
	for j := 1; j < MAXN; j++ {
		pow[j] = pow[j-1] * base
	}
}

// 生成随机子串
func randomString(len int, rangeNum int) string {
	b := make([]byte, len)
	for i := 0; i < len; i++ {
		b[i] = byte(rand.Intn(rangeNum) + 'a')
	}
	return string(b)
}

func main() {
	N := 100
	M := 50
	K := 10
	// a b c
	// R =4 abcd
	R := 3
	testTimes := 10000
	fmt.Println("测试开始")
	for i := 0; i < testTimes; i++ {
		n := rand.Intn(N) + 1
		m := rand.Intn(M) + 1
		k := rand.Intn(K)
		str1 := randomString(n, R)
		str2 := randomString(m, R)
		ans1 := howMany1(str1, str2, k)
		ans2 := howMany2(str1, str2, k)
		if ans1 != ans2 {
			fmt.Println("出错了!")
		}
	}
	fmt.Println("测试结束")
}

在这里插入图片描述

标签:11,源串,匹配,复杂度,模式,算法,哈希,子串
From: https://www.cnblogs.com/moonfdd/p/17826255.html

相关文章

  • 20211316郭佳昊 《信息安全系统设计与实现(上)》 第十周学习总结
    一、任务要求[1]知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容(4分)我在学****知识点,请你以苏格拉底的方式对我进行提问,一次一个问题核心是要求GPT:请你以苏格拉底的方式对我进行提问然后GPT就会......
  • 11.11
    警告,今天闲话有物理学术内容。游泳池是食堂,食堂游泳池是用来洗手的,食堂浴室是用来游泳的。昨天晚上回教室路上发现下雪了,真高兴啊终于这狗学校下雪了......
  • 2023.11.11——每日总结
    学习所花时间(包括上课):9h代码量(行):0行博客量(篇):1篇今天,上午学习,下午学习;我了解到的知识点:1.mybatis明日计划:学习......
  • 学习笔记11(第十三章)
    #一、知识点归纳##(一)知识点内容###教材学习内容总结##(二)苏格拉底挑战###1.知识点一###2.知识点二#二、问题与解决##(一)问题##(二)解决#三、实践过程与代码##(一)实践##(二)代码......
  • 20231111练习
    2023-11-11T1【GDOI2017模拟7.19】小X调顺序ProblemDescriptionInputOutputSampleInputCopy31221SampleOutputCopy1DataConstraint求逆序对然后减去\(k\)即可,思维题。#include<cstdio>#include<algorithm>#definelllonglong#defineN1000......
  • 11 11 vue3代码优化
     使用axios发送异步请求是这种格式,现在异步请求都封装到api中。说法如下:接口调用的js代码一般都会封装到js文件中,并一函数的形式暴露给外部,例如: 这张图片包括了没有参数和有参数的两种情况 然后在组件中的script中调用函数就行,但这样不行,好像跟什么同步异步有关,反正这样......
  • 协议分析11 RIP
    协议分析11RIP更好的阅读体验:https://type.dayiyi.top/index.php/archives/248/https://blog.dayi.ink/?p=98https://cnblogs.cn/rabbit-dayihttps://cmd.dayi.ink/nKnLEJJuQACUW2qAPCjbAQ让我R.I.P,吧,11个作业。插电,开机步骤和要求1)选择样例中的RIPv1&v2,打开网络拓......
  • 11.11日记
    二叉查找树在频繁的动态更新过程中,可能会出现树的高度远大于log2n的情况,从而导致各个操作的效率下降。极端情况下,二叉树会退化为链表,时间复杂度会退化到O(n)。我上一节说了,要解决这个复杂度退化的问题,我们需要设计一种平衡二叉查找树,也就是今天要讲的这种数据结构。很多书籍里,但凡......
  • 11.10每日总结
    完全引入//1.安装依赖-生产依赖npminstallelement-ui-S12//main.jsimportVuefrom'vue';importElementUIfrom'element-ui';//2.1引入结构import'element-ui/lib/theme-chalk/index.css';//2.2引入样式importAppfrom'./App.vue';Vue.us......
  • win11命令行恢复win10右键菜单和资源管理器
    前言随着Windows11的发布,许多新的特性和改变引起了用户的广泛关注。然而,对于一些习惯了Windows10操作界面的用户来说,他们可能更希望保留Windows10的一些特性,比如右键菜单和资源管理器。本文将详细介绍如何在Windows11中通过命令行恢复Windows10的右键菜单和资源管理器,帮助用......