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的更多相关文章

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

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

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

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

  3. C语言字符串操作函数集

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

  4. C语言字符串操作详细总结

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

  5. 面试之C语言字符串操作总结大全(转载)

    趁着十一就好好补补数据结构吧,通信这个不软不硬的专业,现在还是得好好学学补习补习,,你这个非211的本科生!虽然拿到了一个offer,但是觉得时间还有,得继续拼一拼,希望不辜负! 1)字符串操作 st ...

  6. C语言学习笔记 (008) - C语言字符串操作总结大全(超详细)(转)

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

  7. C语言字符串操作总结大全(超具体)

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

  8. C语言字符串操作

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

  9. [转]C语言字符串操作总结大全(超详细)

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

  10. 07 --C语言字符串函数

    1)字符串操作  复制 strcpy(p, p1)      复制字符串 strncpy(p, p1, n)  复制指定长度字符串 strdup(char *str)      将串拷贝到新建的位置处 ...

随机推荐

  1. Java8 函数式编程stream流

    1.初始环境准备 ​ 场景:现在有一个公司,公司部门有一级部门,二级部门甲和二级部门乙(其中二级部门甲和二级部门乙是一级部门的子部门), 一级部门下面有有001号员工小明,二级部门甲下面有002号员工 ...

  2. 一种flink 作业提交失败的情况描述与原因排查

    遇到异常 2019-12-24 16:49:59,019 INFO org.apache.flink.yarn.YarnClusterClient - Starting client actor sy ...

  3. GGTalk 开源即时通讯系统源码剖析之:客户端全局缓存及本地存储

    继上篇<GGTalk 开源即时通讯系统源码剖析之:虚拟数据库>详细介绍了 GGTalk 内置的虚拟的数据库,无需部署真实数据库便能体验GGTalk的全部功能,虚拟数据库将极大地简化服务端的 ...

  4. 文心一言 VS 讯飞星火 VS chatgpt (77)-- 算法导论7.3 2题

    二.如果用go语言,在 RANDOMIZED-QUICKSORT 的运行过程中,在最坏情况下,随机数生成器 RANDOM 被调用了多少次?在最好情况下呢?以θ符号的形式给出你的答案? 文心一言: 在 ...

  5. .NET 操作 TDengine .NET ORM

    TDengine 是国内比较流的时序库之一,支持群集并且免费,在.NET中资料比较少,这篇文章主要介绍SqlSugar ORM来操作TDengine 优点: 1.SqlSugar支持ADO.NET操作 ...

  6. 物理服务器不重启分配raid

    一.MegaCli 命令的安装及使用 目录 一.MegaCli 命令的安装及使用 1.下载rpm包 2.安装 3.安装完,就会在/opt/下创建个MegaRAID目录,文件都在里面 4.添加软连接 5 ...

  7. HTML视频背景(动态背景)

    网页动态背景一般是用视频实现的,能增添网页的感染力,我觉得很好看,也不难,不妨学一下. 先加入下面一串代码: 1 <style> 2 video{ 3 height: 100%; 4 wi ...

  8. 文心一言 VS 讯飞星火 VS chatgpt (91)-- 算法导论8.3 4题

    四.用go语言,说明如何在 O(n) 时间内,对 0 到 $n^3-1$ 区间内的n个整数进行排序. 文心一言: 在 O(n) 时间内对 0 到 n^3-1 区间内的 n 个整数进行排序,可以使用基数 ...

  9. 遥遥领先.NET 7, .NET 8 性能大幅提升

    每个版本必有的性能提升汇总文章又来了.大家可以学习阅读了. 微软 .NET 开发团队的工程师 Stephen Toub 发表博客<Performance Improvements in .NET ...

  10. Solution Set -「ABC 205」

    应该是最近最水的 ABC 了吧. 「ABC 205A」kcal Link. 略 #include <bits/stdc++.h> using ll = long long; #define ...