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) 将串拷贝到新建的位置处 ...
随机推荐
- linux文本编辑YCM报错
linux文本编辑YCM报错 刚从github安装了vimplus,可是发现存在不少的问题.索性给直接记录一下. The ycmd server SHUT DOWN (restart with ':Y ...
- 解决pyinstaller生成的exe文件,在部分电脑无法运行的问题
下载vc_redist.x64.exe并在不能运行的电脑上运行即可. vc_redist.x64.exe的作用:一款Visual C++的运行库,里面包含了一些Visual C++的库函数.Visua ...
- [jenkins]连接git仓库
连接gitlab 在jenkins添加全局凭据,类型为"ssh username with private key",填写git用户名和ssh私钥信息(注意是git客户端所在机器的 ...
- SpringBoot3分库分表
标签:ShardingSphere5.分库.分表: 一.简介 分库分表的设计和实现方式,在之前的内容中总结过很多,本文基于SpringBoot3和ShardingSphere5框架实现数据分库分表的能 ...
- erlang和rabbitMq在ubuntu上的安装过程
安装rabbitMQ的前提是安装上erlang,所以从erlang安装开始. 安装erlang 1,先升级一下 $:sudo apt-get update 如果软件源有问题 修改etc/apt/sou ...
- 【LaTeX】环境配置以及中文支持
目录 网页环境 Overleaf 本地环境 TeX Live TeXstudio VSCode 安装 LaTeX Workshop 扩展 编译链配置 正向同步 反向同步 其他可选配置 中文支持 XeL ...
- 《SQL与数据库基础》04. SQL-DQL
目录 DQL 基础查询 条件查询 分组聚合 聚合函数 分组查询 结果排序 分页限制 总结 本文以 MySQL 为例 DQL 语法结构: SELECT 字段列表 FROM 表名列表 WHERE 条件列表 ...
- 如何使用Python进行投资收益和风险分析
如何投资是现代企业.个人投资者所面临的实际问题,投资的目标是收益尽可能大,但是投资往往伴随着风险,如果在保证收益最大化的情况下,风险最小:或是风险相同的情况下,如何实现收益的最大化:通过本实训,可以使 ...
- Kruskal重构树 学习笔记
Kruskal 重构树 最大生成树将部分内容倒置即可 回顾:Kruskal 基本信息 求解最小生成树 时间复杂度:\(O(m \log m)\) 更适合稀疏图 算法思想 按照边权从小到大排序 依次枚举 ...
- Mysql忘记密码后如何重置密码
长时间不使用本机的Mysql后把密码忘记了咋整?直接上干货: 第一步(Mysql部署的位置,若自己能找到就忽略这一步):任务管理器中也可以找到 第二步:修改配置文件 在my.ini末尾加上 skip- ...