首页 > 其他分享 >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

时间:2023-11-08 21:01:47浏览次数:37  
标签:11 hash int 质数 课上 l2 str l1 次方

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("测试结束")
}

2023-11-08:用go语言,字符串哈希原理和实现 比如p = 233, 也就是课上说的选择的质数进制 “ 3 1 2 5 6 ...“ 0 1 2 3 4 hash[0] = 3 * p的0_i++

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_bc_02

标签:11,hash,int,质数,课上,l2,str,l1,次方
From: https://blog.51cto.com/moonfdd/8258264

相关文章

  • 11.8算法
    题目二叉树的中序遍历给定一个二叉树的根节点root,返回它的中序 遍历。示例1:输入:root=[1,null,2,3]输出:[1,3,2]示例2:输入:root=[]输出:[]示例3:输入:root=[1]输出:[1]提示:树中节点数目在范围[0,100]内-100<=Node.val<=100进阶: 递归算法很简单,你......
  • 每日总结20231108
    代码时间(包括上课)6h代码量(行):100行博客数量(篇):1篇相关事项:1、今天是周三,上午上的是软件构造,讲的是交互和测试,具体的交互内容包括和测试的方式包括。2、今天下午参加了一个电气院的用电安全知识竞赛。3、今天晚上打算复习复习数学,毕竟马上要考研。......
  • 11.8每日总结
    今天看公众号,有大神分析了这样的一篇文章:一键生成前端UI,公司90%项目UI都靠它搞定地址:https://mp.weixin.qq.com/s/UhmLwVeZ0jwZORur8XD2MQ并且关注了GPT最新的发布会,好慌张,GPT这是要告诉所有研发大模型的都不要研发了,用他的接口就可以了。......
  • 11月8每日打卡
    实验1熟悉常用的Linux操作和Hadoop操作1.实验目的Hadoop运行在Linux系统上,因此,需要学习实践一些常用的Linux命令。本实验旨在熟悉常用的Linux操作和Hadoop操作,为顺利开展后续其他实验奠定基础。2.实验平台(1)操作系统:Linux(建议Ubuntu16.04或Ubuntu18.04);(2)Hadoop版本:3.1.3。3.......
  • 11.08县哗
    今天只能待到8点半......
  • 11/8训练笔记
    P6273[eJOI2017]魔法题解考虑定义\(S_{r_k}=\Sigma_{i=1}^{r}[s_i=k]\),那么对于任意一个子串\([l,r]\),其为有魔法的子串的充要条件为\(S_{c_{r}}-S_{c_{l-1}}\)对于任意的,在\(s\)中出现了的\(c\)为定值。任取一个在\(s\)中出现了的字符\(A\),那么上述充要条件可转换......
  • 231108校内赛
    T1最大公约数数据极水,啥都能过第一种方法,暴力剪枝,直接飞过,\(\mathcalO(N^2\logn)\)过\(30\)万不解释玛德有人在提交时不写输出直接爆零#include<bits/stdc++.h>#defineN300010usingnamespacestd;intn,k,ans,a[N];intgcd(inta,intb){ returnb==0?a:gcd(b,......
  • 11月7日css介绍、基本格式、样式、选择器
    目录1.css介绍2.css基本格式3.css的几种引入方式1.行内样式2.内部样式3.外部样式css选择器基本选择器1.元素(标签)选择器2.id选择器3.类选择器通用选择器组合选择器1.后代选择器2.子元素选择器3.相邻兄弟选择器通用兄弟选择器属性选择器分组选择器伪类选择器第一个实例给未访问的链......
  • win11不能打开gpedit.msc
    创建文件test.cmd,然后把以下代码复制到此文件,然后用管理员身份运行就可以打开gpedit.msc@echooffpushd"%~dp0"dir/bC:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum>List.txtdir/bC:\Windows\servicing\Packages\M......
  • 【2023.11.08】NOIP2023模拟试题-30
    前言数论迎我归,数学送我葬组合数学不容易,又有DP当T3刚爆零,T4又遭殃OI路上怅前望,且行且彷徨T1最大公约数T1应该想一想就会,接下来我们讨论是怎么减去他的复杂度的。题目的关键在于,如果根据给出的\(a\)推出\(\gcd\)的话,就会有\(9\times10^{10}\)条推导关系。而......