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("测试结束")
}

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

  1. 【CodeForces】961 F. k-substrings 字符串哈希+二分

    [题目]F. k-substrings [题意]给定长度为n的串S,对于S的每个k-子串$s_ks_{k+1}...s_{n-k+1},k\in[1,\left \lceil \frac{n}{2} ...

  2. C语言字符串之无重复字符的最长子串

    题目描述 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 输入: "abcabcbb" 输出: 解释: 因为无重复字符的最长子串是 . 输入: " ...

  3. C语言字符串操作总结大全(超详细)

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作  strcpy(p, p1) 复制字符串  strncpy(p, p1, n) 复制指定长度字符串  strcat( ...

  4. C语言字符串与数字相互转换

    在C/C++语言中没有专门的字符串变量,通常用字符数组来存放字符串.字符串是以“\0”作为结束符.C/C++提供了丰富的字符串处理函数,下面列出了几个最常用的函数. ● 字符串输出函数puts. ● ...

  5. C 语言 字符串命令 strstr()的用法 实现将原字符串以分割串分割输出

    C 语言 字符串命令 strstr()的用法 实现将原字符串以分割串分割输出 strstr() 命令是在原字符串中查找指定的字符串第一次出现的地址,用这个特性可以实现字符的分割,判断是否包涵等功能: ...

  6. 【转】C语言字符串与数字相互转换

    在C/C++语言中没有专门的字符串变量,通常用字符数组来存放字符串.字符串是以“\0”作为结束符.C/C++提供了丰富的字符串处理函数,下面列出了几个最常用的函数. ● 字符串输出函数puts. ● ...

  7. C.【转】C语言字符串与数字相互转换

    1.gcvt 把浮点数转成字符串 - CSDN博客.html(https://blog.csdn.net/dxuehui/article/details/52791412) 1.1. 函数名: gcv ...

  8. Strsafe.h:更安全的C语言字符串处理函数

    原文出处:Strsafe.h: Safer String Handling in C 作者:Michael Howard 编译:王凌峰 在微软公司举行的Microsoft Windows Securi ...

  9. 零基础学习C语言字符串操作总结大全

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, ...

  10. C语言字符串操作总结大全

    1)字符串操作 strcpy(p, p1)  复制字符串  函数原型strncpy(p, p1, n)   复制指定长度字符串  函数原型strcat(p, p1)   附加字符串  函数原型strn ...

随机推荐

  1. 自动刷新服务:nodemon

    安装命令: npm install -g nodemon 运行命令: nodemon server.js 运行结果:

  2. 糟了糟了,总部被SD画完都Q了,这篇深入浅出贴助你早日实现Stable Diffusion自由

    我也不想标题党,可乐高积木版的总部大楼就是好萌啊! 我是憨憨,一个不会画画的设计师.过去半年里,AI绘画曾经多次引爆公众讨论,网络上那些精致的二次元同人插画.堪比真人的AI穿搭博主.打破次元壁的赛博C ...

  3. 使用C#创建安装Windows服务程序(最全教程)

    开发语言:C# 开发环境: Visual Studio 2022 微软官方文档:https://learn.microsoft.com/zh-cn/dotnet/framework/windows-s ...

  4. python机器学习经典算法代码示例及思维导图(数学建模必备)

    最近几天学习了机器学习经典算法,通过此次学习入门了机器学习,并将经典算法的代码实现并记录下来,方便后续查找与使用. 这次记录主要分为两部分:第一部分是机器学习思维导图,以框架的形式描述机器学习开发流程 ...

  5. Kurator,你的分布式云原生解决方案

    本文分享自华为云社区<DTSE Tech Talk | 第40期:Kurator,你的分布式云原生解决方案>,作者:华为云社区精选. 什么是分布式云原生? 中国信通院给出的定义:分布式云原 ...

  6. 让你的HpSocket Pull支持同步(应答式)操作

    什么是HPSocket HP-Socket 是一套通用的高性能 TCP/UDP 通信框架,包含服务端组件.客户端组件和Agent组件,广泛适用于各种不同应用场景的 TCP/UDP 通信系统,提供 C/ ...

  7. 【后端面经-数据库】Redis数据结构和底层数据类型

    目录 1. Redis数据类型 1.1 基本数据类型 1. string 2. hash 3. list 4. set 5. sortset/Zset 1.2 特殊数据类型 1. bitmap 2. ...

  8. 使用KRPano资源分析工具强力加密JS文件

    本文地址:http://www.cnblogs.com/reachteam/p/6294767.html 软件交流群:571171251(软件免费版本在群内提供) krpano技术交流群:551278 ...

  9. 通过 Haproxy 实现 ss 负载均衡

    介绍 缺点:所有的SS的加密方式和密码必须一致 介绍:HAProxy是一个使用C语言编写的自由及开放原始码软件,其提供高可用性.负载均衡,以及基于TCP和HTTP的应用程序代理. 安装Haproxy ...

  10. C++的extern关键字在HotSpot VM中的重要应用

    extern关键字有两个用处: (1)extern在C/C++语言中表示函数和全局变量作用范围(可见性)的关键字,这个关键字会告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用. (2)在C+ ...