2023-11-08:用go语言,字符串哈希原理和实现 比如p = 233, 也就是课上说的选择的质数进制 “ 3 1 2 5 6 ...“ 0 1 2 3 4 hash[0] = 3 * p的0
2023-11-08:用go语言,字符串哈希原理和实现
比如p = 233, 也就是课上说的选择的质数进制
" 3 1 2 5 6 ..."
0 1 2 3 4
hash[0] = 3 * p的0次方
hash[1] = 3 * p的1次方 + 1 * p的0次方
hash[2] = 3 * p的2次方 + 1 * p的1次方 + 2 * p的0次方
hash[3] = 3 * p的3次方 + 1 * p的2次方 + 2 * p的1次方 + 5 * p的0次方
hash[4] = 3 * p的4次方 + 1 * p的3次方 + 2 * p的2次方 + 5 * p的1次方 + 6 * p的0次方
次方是倒过来的,课上讲错了
所以hash[i] = hash[i-1] * p + arr[i],这个方式就可以得到上面说的意思
于是,你想得到子串"56"的哈希值
子串"56"的哈希值 = hash[4] - hash[2]*p的2次方(就是子串"56"的长度次方)
hash[4] = 3 * p的4次方 + 1 * p的3次方 + 2 * p的2次方 + 5 * p的1次方 + 6 * p的0次方
hash[2] = 3 * p的2次方 + 1 * p的1次方 + 2 * p的0次方
hash[2] * p的2次方 = 3 * p的4次方 + 1 * p的3次方 + 2 * p的2次方
所以hash[4] - hash[2] * p的2次方 = 5 * p的1次方 + 6 * p的0次方
这样就得到子串"56"的哈希值了
抱歉,课上讲错了。应该是上面的方式。
所以,子串s[l...r]的哈希值 = hash[r] - hash[l-1] * p的(r-l+1)次方
也就是说,hash[l-1] * p的(r-l+1)次方,正好和hash[r]所代表的信息,前面对齐了
减完之后,正好就是子串s[l...r]的哈希值。
来自左程云。
答案2023-11-08:
go和c++代码用灵捷3.5编写,不需要修改。
大体过程如下:
rightCheck函数的过程:
1.检查l1和l2是否超出字符串边界,如果超出则返回false。
2.如果l1和l2相等,则直接返回true。
3.判断从l1开始长度为length的子串和从l2开始长度为length的子串是否相等,如果相等则返回true,否则返回false。
hashCheck函数的过程:
1.计算l1到r1和l2到r2两个子串的哈希值。
2.检查r1和r2是否超出字符串边界,如果超出则返回false。
3.根据哈希值判断两个子串是否相等,如果相等则返回true,否则返回false。
rightCheck函数的时间复杂度:O(length)
hashCheck函数的时间复杂度:O(1)
rightCheck函数的额外空间复杂度:O(1)
hashCheck函数的额外空间复杂度:O(1)
go完整代码如下:
package main
import (
"fmt"
"math/rand"
)
const MAXN = 100005
var pow [MAXN]int64
var hash [MAXN]int64
var base = 499
func rightCheck(str string, l1 int, l2 int, length int) bool {
if l1+length > len(str) || l2+length > len(str) {
return false
}
if l1 == l2 {
return true
}
return str[l1:l1+length] == str[l2:l2+length]
}
func build(str string, n int) {
pow[0] = 1
for j := 1; j < n; j++ {
pow[j] = pow[j-1] * int64(base)
}
hash[0] = int64(str[0]-'a') + 1
for j := 1; j < n; j++ {
hash[j] = hash[j-1]*int64(base) + int64(str[j]-'a') + 1
}
}
func hashCheck(n, l1, l2, length int) bool {
r1 := l1 + length - 1
r2 := l2 + length - 1
if r1 >= n || r2 >= n {
return false
}
return hashf(l1, r1) == hashf(l2, r2)
}
func hashf(l, r int) int64 {
var ans int64
ans = hash[r]
if l == 0 {
ans -= 0
} else {
ans -= hash[l-1] * pow[r-l+1]
}
return ans
}
func randomString(length, v int) string {
str := make([]byte, length)
for i := 0; i < length; i++ {
str[i] = byte('a' + (int64(v)*int64(i))%26)
}
return string(str)
}
func main() {
test := "abcabcabcabcabcabcabcabc"
size := len(test)
build(test, size)
fmt.Println(hashCheck(size, 6, 15, 3))
fmt.Println("测试开始")
N := 10000
V := 3
testTeams := 100
testTimes := 5000
LEN := 6
for i := 0; i < testTeams; i++ {
n := (int)(rand.Float64()*float64(N)) + 1
str := randomString(n, V)
build(str, n)
for k := 0; k <= testTimes; k++ {
l1 := (int)(rand.Float64() * float64(n))
l2 := (int)(rand.Float64() * float64(n))
length := (int)(rand.Float64()*float64(LEN)) + 1
ans1 := rightCheck(str, l1, l2, length)
ans2 := hashCheck(n, l1, l2, length)
if ans1 != ans2 {
fmt.Println("出错了!")
break
}
}
}
fmt.Println("测试结束")
}

c++完整代码如下:
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
const int MAXN = 100005;
long long pow0[MAXN];
long long hashArr[MAXN];
int base = 499;
bool rightCheck(string str, int l1, int l2, int len) {
if (l1 + len > str.length() || l2 + len > str.length()) {
return false;
}
if (l1 == l2) {
return true;
}
return str.substr(l1, len) == str.substr(l2, len);
}
void build(string str, int n) {
pow0[0] = 1;
for (int j = 1; j < n; j++) {
pow0[j] = pow0[j - 1] * base;
}
hashArr[0] = str[0] - 'a' + 1;
for (int j = 1; j < n; j++) {
hashArr[j] = hashArr[j - 1] * base + str[j] - 'a' + 1;
}
}
bool hashCheck(int n, int l1, int l2, int len) {
int r1 = l1 + len - 1;
int r2 = l2 + len - 1;
if (r1 >= n || r2 >= n) {
return false;
}
return hashArr[l1 + len - 1] - (l1 == 0 ? 0 : hashArr[l1 - 1] * pow0[len]) == hashArr[l2 + len - 1] - (l2 == 0 ? 0 : hashArr[l2 - 1] * pow0[len]);
}
string randomString(int len, int v) {
string str;
for (int i = 0; i < len; i++) {
str += char('a' + rand() % v);
}
return str;
}
int main() {
string test = "abcabcabcabcabcabcabcabc";
int size = test.length();
build(test, size);
cout << hashCheck(size, 6, 15, 3) << endl;
cout << "测试开始" << endl;
int N = 10000;
int V = 3;
int testTeams = 100;
int testTimes = 5000;
int LEN = 6;
for (int i = 0; i < testTeams; i++) {
int n = rand() % N + 1;
string str = randomString(n, V);
build(str, n);
for (int k = 0; k <= testTimes; k++) {
int l1 = rand() % n;
int l2 = rand() % n;
int len = rand() % LEN + 1;
bool ans1 = rightCheck(str, l1, l2, len);
bool ans2 = hashCheck(n, l1, l2, len);
if (ans1 != ans2) {
cout << "出错了!" << endl;
break;
}
}
}
cout << "测试结束" << endl;
return 0;
}

2023-11-08:用go语言,字符串哈希原理和实现 比如p = 233, 也就是课上说的选择的质数进制 “ 3 1 2 5 6 ...“ 0 1 2 3 4 hash[0] = 3 * p的0的更多相关文章
- C语言字符串操作总结大全
1)字符串操作 strcpy(p, p1) 复制字符串 函数原型strncpy(p, p1, n) 复制指定长度字符串 函数原型strcat(p, p1) 附加字符串 函数原型strn ...
- C语言字符串操作总结大全(超详细)
本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat( ...
- C语言字符串操作函数集
1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度 ...
- C语言字符串操作详细总结
1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度 ...
- 面试之C语言字符串操作总结大全(转载)
趁着十一就好好补补数据结构吧,通信这个不软不硬的专业,现在还是得好好学学补习补习,,你这个非211的本科生!虽然拿到了一个offer,但是觉得时间还有,得继续拼一拼,希望不辜负! 1)字符串操作 st ...
- C语言学习笔记 (008) - C语言字符串操作总结大全(超详细)(转)
1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度 ...
- C语言字符串操作总结大全(超具体)
1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度 ...
- C语言字符串操作
1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度 ...
- [转]C语言字符串操作总结大全(超详细)
1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度 ...
- 07 --C语言字符串函数
1)字符串操作 复制 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strdup(char *str) 将串拷贝到新建的位置处 ...
随机推荐
- 通过Maxwell同步mariadb数据至kafka
实验环境 本地虚拟机 maraidb 10.8.8 kafka 2.12-3.3.1 maxwell由容器部署 1 mariadb 1.1 配置log_bin 配置文件中加入如下内容 server-i ...
- fastposter v2.16.0 让海报开发更简单
fastposter v2.16.0 让海报开发更简单 fastposter海报生成器是一款快速开发海报的工具.只需上传一张背景图,在对应的位置放上组件(文字.图片.二维.头像) 点击代码直接生成各种 ...
- 2022-02-08 IValueConverter和StringFormat
主页 后台 stringFormat
- Java中的线程池使用及原理
开篇-为什么要使用线程池? Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来 3 个好处. 第一:降低 ...
- 秋叶整合包如何安装Python包
前几天写了一篇<手把手教你在本机安装Stable Diffusion秋叶整合包>的文章,有同学运行时遇到缺少Python Module的问题,帮助他处理了一下,今天把这个经验分享给大家,希 ...
- Ceres简单应用-求解(Powell's Function)鲍威尔函数最小值
Ceres 求解 Powell's function 的最小化 \(\quad\)现在考虑一个稍微复杂一点的例子-鲍威尔函数的最小化. \(\quad{}\) \(x=[x_1,x_2,x_3,x_4 ...
- centos7离线安装harbor
前言 harbor是一个docker私有仓库,基于docker官方的registry,提供GUI.权限控制.项目管理等功能. 安装harbor前,需要先安装docker和docker-compose ...
- 聊一下操作系统-macOS-与-Linux
聊一下操作系统 macOS 与 Linux 对于Windows操作系统大家都比较熟悉,也常拿它与Linux操作系统进行比较,两者之间的差异也很明显.但对于macOS 和 Linux的比较不太多,很多人 ...
- Linux-源码安装软件
一.源码安装步骤 源码的安装一般由3个步骤组成:配置(configure).编译(make).安装(make install). 1.配置(configure) Configure是一个可执行脚本,它 ...
- mall :rabbit项目源码解析
目录 一.mall开源项目 1.1 来源 1.2 项目转移 1.3 项目克隆 二.RabbitMQ 消息中间件 2.1 rabbit简介 2.2 分布式后端项目的使用流程 2.3 分布式后端项目的使用 ...