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. MAUI Blazor 显示本地图片的新思路

    前言 好久没写文章了,水一篇 关于MAUI Blazor 显示本地图片这个问题,有大佬发过了. 就是 token 大佬的那篇 Blazor Hybrid (Blazor混合开发)更好的读取本地图片 主 ...

  2. Argument data type text is invalid for argument 1 of replace function

    今天给webapi添加了搜索,结果数据库显示了这个错误, Argument data type text is invalid for argument 1 of replace function 查 ...

  3. Socket 入坑

    什么是Socket Socket(套接字)是在计算机网络中实现通信的一种机制.它提供了一种应用程序编程接口(API),允许应用程序通过网络进行数据传输和通信. 在网络通信中,Socket 可以被看作是 ...

  4. 【JMeter】JMeter添加插件

    JMeter添加插件 目录 JMeter添加插件 一.前言 二.插件管理器 三.推荐插件 1.Custom Thread Groups (1)Ultmate Thread Group (2)Stepp ...

  5. 策略模式+Spring配置类优化多if..else思路

    图示 1. 现状 场景: 假设设备上报不同类型的消息,我们要对不同类型的消息做不同的处理.如果我们通过if..else的方式处理的话会显得比较冗余. 例如: if("alarmEvent&q ...

  6. linux vim 无权限保存解决办法

    通常在vim编辑文件时往往会忘记文件权限问题, 在wq保存时发现权限不足,这时候输入以下命令解决: w! sudo tee % 命令解析: w! {cmd} 指示 保存时执行额外命令: tee 用于将 ...

  7. [golang]获取本机IP

    前言 方便在内网环境中获取服务器本机IP,省了在脚本中过滤ip或ifconfig的结果. 如果内网中有nginx的话,通过nginx获取本机IP也很方便,可参考 借助nginx自动获取本机IP 示例代 ...

  8. [python]爬取手机号码前缀和地区信息

    概述 使用python爬取手机号码前缀7位.区号和地区. 小网站不容易,对爬虫也挺友好,就不放链接了. 代码 import requests from lxml import etree from f ...

  9. 使用kafka自带脚本进行压力测试

    前言 kafka官方自带压力测试脚本: 消费者压力测试:kafka-consumer-perf-test.sh 生产者压力测试:kafka-producer-perf-test.sh 测试节点: 17 ...

  10. Spring面试攻略:如何展现你对Spring的深入理解

    什么是Spring?谈谈你对IOC和AOP的理解. Spring是一种Java开发框架,旨在简化企业级应用程序的开发和部署.它具有以下优点: 对象托管:Spring能够管理和赋值所有对象,使开发人员不 ...