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. python入门,一篇就够了

    python规范 函数必须写注释:文档注释格式'''注释内容''' 参数中的等号两边不要用空格 相邻函数用两个空行隔开 小写 + 下划线 函数名 模块名 实例名 驼峰法 类名 tips # 一行代码太 ...

  2. 利用pytorch自定义CNN网络(一):torchvision工具箱

    本文是利用pytorch自定义CNN网络系列的第一篇,主要介绍 torchvision工具箱及其使用,关于本系列的全文见这里. 笔者的运行设备与软件:CPU (AMD Ryzen 5 4600U) + ...

  3. win10安装mysql5.7.35教程

    前提条件:我下载的是压缩包版本5.7.35,下载地址是 https://downloads.mysql.com/archives/community/ 下载完后解压,并在如下图目录里加入data文件夹 ...

  4. AVR汇编(七):位操作和MCU控制指令

    AVR汇编(七):位操作和MCU控制指令 位操作指令 SBI / CBI SBI 指令用于设置I/O寄存器中的第 b 位, CBI 指令用于清除I/O寄存器中的第 b 位. 例如: SBI DDRB, ...

  5. 解放双手!ChatGPT助力编写JAVA框架

    亲爱的Javaer们,在平时编码的过程中,你是否曾想过编写一个Java框架去为开发提效?但是要么编写框架时感觉无从下手,不知道从哪开始.要么有思路了后对某个功能实现的技术细节不了解,空有想法而无法实现 ...

  6. PRACK消息

    概述 PRACK消息是sip协议的扩展,在RFC3262中定义,标准的名称是sip协议中的可靠临时响应. 本文简单介绍标准中对PRACK消息流程的描述,以及fs配置PRACK的方式. 环境 cento ...

  7. 修改内置框架css 样式

    <style scoped> 1 <style scoped> 2 .info /deep/ .video{ // info 外层便签 /deep/ 可以理解为连接桥 .vid ...

  8. 【微信自动化】使用c#实现微信自动化

    引言 上个月,在一个群里摸鱼划水空度日,看到了一个老哥分享的一个微信自动化的一个类库,便下载了他的Demo,其本意就是模拟鼠标来操作UI,实现UI自动化:然后自己在瞎琢磨研究,写了一个简单的例子,用来 ...

  9. [Mysql] 存储过程简单理解

    什么是存储过程 简单的说, 就是一组SQL语句集, 功能强大, 可以实现一些比较复杂的逻辑功能. 其实就和编程语言的面向过程函数一样. ps: 存储过程与触发器类似, 但存储过程是主动调用, 触发器是 ...

  10. 钉钉旧版服务端SDK支持异步方法的升级改造

    最近项目中需要对接钉钉,有些钉钉 API 的访问需要使用旧版服务端 SDK 才能搞定,但是这个 SDK 使用的还是 .NET Framework 2.0 框架,不能跨平台部署,也不支持 async\a ...