hash函数构建

  • 采取26进制

    对于字符串str,令**H[i] = H[i-1]*26 + index(str[i]) **,最后H[i-1]就是str的hash值

    问题:hash值过大,无法表式

  • 取模

    在上述基础上取模:H[i] = (H[i-1]*26 + index(str[i]))%mod

    问题:丧失了一定的唯一性

  • 权衡:一个冲突概率极小的hash函数

    H[i] = (H[i-1]*p + index(str[i]))%mod

    其中p=107 数量级的素数(10000019),mod=109 数量级的素数(1000000007)

例1:判断不同的字符串个数

问题描述

给出N个只有小写字母的字符串,判断其中不同的字符串的个数

代码实现

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
const int MOD = 1000000007;
const int P = 10000019;
vector<int> ans;
//字符串hash
long long hashFunc(string str){
long long H = 0;
for(int i = 0; i < str.length(); i++){
H = (H*P + str[i] - 'a') % MOD;
}
return H;
}
int main(){
string str;
while(getline(cin, str), str != "#"){
long long id = hashFunc(str);
ans.push_back(id);
}
sort(ans.begin(), ans.end());
int count = 0;
for(int i = 0; i < ans.size(); i++){
if(i == 0 || ans[i] != ans[i-1])
count++;
}
cout<< count << endl;
return 0;
}

例2: 最长公共子串

前置:求解子串str[i…j]的hash值H[i…j]

符号含义:

  • H[i..j] : str[i]~str[j]这一子串对应的hash值,即该子串对应的p进制数
  • H[i] : H[0…i]

H[i…j] = index(str[i]) * pj-i + index(str[i-1]) * pj-i-1 + … + index(str[j]) * p0

H[i] = H[i-1] * p + index(str[i])

于是 :

所以:

H[i…j] = H[j] - H[i - 1] * pj-i+1求完str的H数组后,直接调取下标j 和 i-1 即可求得

取模:

H[i…j] = (H[j] - H[i - 1] * pj-i+1)%mod

非负处理:(括号内可能为负值)加模再取模

H[i…j] = ((H[j] - H[i - 1] * pj-i+1)%mod + mod)%mod

步骤

  1. 计算H[]数组
  2. 求出两个字符串所有子串的hash值以及对应的长度
  3. 子串两两比较,得出长度最大值

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL MOD = 1000000007;
const LL P = 10000019;
const int MAXN = 1010; //MAXN为字符串的最大长度
//powP[i]存放p^i%MOD, H1,H2分别存放str1,str2的hash值
LL powP[MAXN], H1[MAXN] = {0}, H2[MAXN] = {0};
//pr1存放所有<子串hash值,子串长度>, pr2同理
vector<pair<int,int>> pr1,pr2; //init函数初始化powP
void init(int len){
powP[0] = 1;
for(int i = 1; i <= len; i++){
powP[i] = (powP[i-1]*P)%MOD;
}
} //calH函数计算字符串str的hash值
void calH(LL H[], string &str){
H[0] = str[0];
for(int i = 1; i < str.length(); i++){
H[i] = (H[i-1]*P + str[i])%MOD;
}
}
//calSingleSubH 计算 H[i...j]
int calSingleSubH(LL H[], int i, int j){
if(i == 0) return H[j];
return ((H[j] - H[i-1] * powP[j - i + 1])%MOD + MOD)%MOD;
}
//calSubH 计算 所有子串的hash值,并将<子串hash值,子串长度>存入pr
void calSubH(LL H[], int len, vector<pair<int,int>> &pr){
for(int i = 0; i < len; i++){
for(int j = i; j < len; j++){
int hashValue = calSingleSubH(H, i, j);
pr.push_back(make_pair(hashValue, j - i + 1));
}
}
}
//计算 pr1 和 pr2中相同的 hash值, 维护最大长度
int getMax(){
int ans = 0;
for(int i = 0; i < pr1.size(); i++){
for(int j = 0; j < pr2.size(); j++){
if(pr1[i].first == pr2[j].first)
ans = max(ans, pr1[i].second);
}
}
return ans;
} int main(){
string str1, str2;
getline(cin, str1);
getline(cin, str2);
init(max(str1.length(), str2.length())); //初始化powP数组
calH(H1,str1); //分别计算 str1 和 str2 的hash值
calH(H2,str2);
calSubH(H1,str1.length(),pr1); //分别计算所有H1[i...j] 和 H2[i...j]
calSubH(H2,str2.length(),pr2);
printf("ans = %d", getMax()); //输出最大公共子串长度
return 0;
}

<数据结构>hash进阶的更多相关文章

  1. 《算法竞赛进阶指南》0x10 基本数据结构 Hash

    Hash的基本知识 字符串hash算法将字符串看成p进制数字,再将结果mod q例如:abcabcdefg 将字母转换位数字(1231234567)=(1*p9+2*p8+3*p7+1*p6+2*p5 ...

  2. 数据结构 : Hash Table

    http://www.cnblogs.com/lucifer1982/archive/2008/06/18/1224319.html 作者:Angel Lucifer 引子 这篇仍然不讲并行/并发. ...

  3. php 数据结构 hash表

    hash表 定义 hash表定义了一种将字符组成的字符串转换为固定长度(一般是更短长度)的数值或索引值的方法,称为散列法,也叫哈希法.由于通过更短的哈希值比用原始值进行数据库搜索更快,这种方法一般用来 ...

  4. hash进阶:使用字符串hash乱搞的姿势

    前言 此文主要介绍hash的各种乱搞方法,hash入门请参照我之前这篇文章 不好意思hash真的可以为所欲为 在开头先放一下题表(其实就是我题解中的hash题目qwq) 查询子串hash值 必备的入门 ...

  5. Redis系列(九):数据结构Hash源码解析和HSET、HGET命令

    2.源码解析 1.相关命令如下: {"hset",hsetCommand,,"wmF",,NULL,,,,,}, {"hsetnx",hse ...

  6. 数据结构-Hash表

    实现: #ifndef SEPARATE_CHAINING_H #define SEPARATE_CHAINING_H #include <vector> #include <lis ...

  7. MySQL源码 数据结构hash

    MySQL源码自定义了hash表,因为hash表具有O(1)的查询效率,所以,源码中大量使用了hash结构.下面就来看下hash表的定义: [源代码文件include/hash.h mysys/has ...

  8. 字符串转hash进阶版

    #include<bits/stdc++.h> using namespace std; ,mod=; vector<unsigned> H[mod]; void Add(un ...

  9. Redis系列(九):数据结构Hash之HDEL、HEXISTS、HGETALL、HKEYS、HLEN、HVALS命令

    1.HDEL 从 key 指定的哈希集中移除指定的域.在哈希集中不存在的域将被忽略. 如果 key 指定的哈希集不存在,它将被认为是一个空的哈希集,该命令将返回0. 时间复杂度:O(N) N是被删除的 ...

随机推荐

  1. ALitum技巧

    创建异型焊盘的方法 SCH与PCB同步修改后元器件乱跑的解决方法 Altium 在PCB重新编号更新到SCH原理图的方法 同步问题 其他技巧: 当前层亮色,其他层灰色切换:SHIFT+S

  2. 转 Android中Activity的启动模式(LaunchMode)和使用场景

    转载请注明出处:http://blog.csdn.net/sinat_14849739/article/details/78072401本文出自Shawpoo的专栏我的简书:简书 一.为什么需要启动模 ...

  3. ORACLE lag,lead

    oracle中想取对应列前几行或者后几行的数据时可以使用lag和lead分析函数 lag:是滞后的意思,表示本行数据是要查询的数据后面,即查询之前行的记录. lead:是领队的意思,表示本行数据是要查 ...

  4. ORACLE 获取执行计划的方法

    一.获取执行计划的6种方法(详细步骤已经在每个例子的开头注释部分说明了): 1. explain plan for获取: 2. set autotrace on : 3. statistics_lev ...

  5. Linux 双网卡绑定及Bridge

    Linux 双网卡绑定及Bridge 阅读(5,202) 一:linux操作系统下双网卡绑定有七种模式.现在一般的企业都会使用双网卡接入,这样既能添加网络带宽,同时又能做相应的冗余,可以说是好处多多. ...

  6. SpringBoot的定时任务

    springBoot定时任务可分为多线程和单线程,而单线程又分为注解形式,接口形式 1.基于注解形式 基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影 ...

  7. jQuery遍历的几种方式

    一.jQuery对象遍历 1 <script type="text/javascript" src="js/jquery-3.4.1.js">< ...

  8. 『与善仁』Appium基础 — 24、等待activity出现

    目录 1.什么是等待activity出现 2.wait_activity()方法 3.获取当前页面的activity方法 4.综合练习 1.什么是等待activity出现 在启动APP的时候,要配置包 ...

  9. python selenium 多账户自动登入163邮箱

    pycharm一些快捷键: ' ctrl ' +' / ' :注释 ' Tab ' :同时缩进 ' shift ' +' Tab ' :左移 一次缩进 本文webinfo.txt路径:C:\Pytho ...

  10. 小迪安全 Web安全 基础入门 - 第十天 - 信息打点-APP&小程序篇&抓包封包&XP框架&反编译&资产提取

    一.本节知识点思维导图 二.APP-外在资产收集 1.将APP安装在模拟器中,修改模拟器代理设置,使用Fiddler.Burpsuite.Charles等抓包工具抓取APP访问的http协议数据包,抓 ...