文章目录

  • 目录

    文章目录

    前言:

    一 Hash表

  • 1 Hash函数的构造

  • 2 拉链法处理hash冲突模板

  • 3 开放寻址法处理hash冲突

    4(例题)、雪花雪花

    二   字符串Hash O(n)+O(m)

    1.回文子串的最大长度

    2 后缀数组

    3  矩阵

    4 树形地铁系统(涉及树的知识)

    三 C++ ST库之 unordered_map——哈希表


前言:

在学习本节课之前,请大家思考这样一个问题:如果我们要在一个长度为 的随机整数序列 中统计每 个数出现的次数,可以用什么方法?

不难想到,我们可以建立一个数组 ,然后将整数序列A中的元素映射到数组的下标,最后遍历整个数组 即可统计出随机整数序列 中每个数出现的次数。但是如果数列 中数比较大呢?我们就可以借助Hash 表来解决这个问题。

一 Hash表

散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存储存位置的数据结构。也就是说,它 通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度 当我们要对若干复杂信息进行统计时,可以用Hash函数把这些复杂信息映射到一个容易维护的值域内。

比如刚才那个问题,当N过于大时,我们可以设计Hash函数为 ,其中P是 一个较大的质数,但不超过N,这样这个Hash函数就把数列 中的值映射到了不同的区域进行储存,然 后继续进行后面的操作。

总的来说, 其基本原理是:使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函 数,也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标,hash值)相对应,于是 用这个数组单元来存储这个元素。

但是,由于值域变简单、范围变小,有可能造成两个不同的原始信息被Hash函数映射为相同的值,这种 情况叫做Hash冲突,我们下面会讲解如何处理这种冲突。

总结一下,Hash表主要包括两个基本操作:

1.计算Hash函数的值

2.定位到对应链表中依次遍历、比较。

1 Hash函数的构造

hash函数构造原则

1.简答易算

2.能够使地址均匀分布

直接定址法 H(key)=a*key+b

除留余数法 H(key)=key MOD p,p<=m (最简单,最常用)p的选取尽量是一个质数,并且离2的 整次幂尽可能的远

2 拉链法处理hash冲突模板

拉链法是借助邻接表实现的,当目标元素经过Hash函数计算后所得到的值与之前的元素计算得出的值相 同,就将其插入以这个Hash值开头的链表中去。

#include <cstring>
#include <iostream>using namespace std;const int N = 1e5 + 3;
// 取大于1e5的第一个质数,取质数冲突的概率最小 可以百度//* 开一个槽 h
int h[N], e[N], ne[N], idx;  //邻接表
//分别存储链表头,数值,指向的下一元素编号
void insert(int x) {//插入// c++中如果是负数 那他取模也是负的 所以 加N 再 %N 就一定是一个正数int k = (x % N + N) % N;e[idx] = x;ne[idx] = h[k];h[k] = idx++;
}bool find(int x) {//查找//用上面同样的 Hash函数 讲x映射到 从 0-1e5 之间的数int k = (x % N + N) % N;for (int i = h[k]; i != -1; i = ne[i]) {if (e[i] == x) {return true;}}return false;
}int n;int main() {cin >> n;memset(h, -1, sizeof h);  //将槽先清空 空指针一般用 -1 来表示while (n--) {string op;int x;cin >> op >> x;if (op == "I") {insert(x);} else {if (find(x)) {puts("Yes");} else {puts("No");}}}return 0;
}

3 开放寻址法处理hash冲突

开放寻址法指的是当你发现这个位置已经被占用后,就按照一定的喜好去寻找没有被占用的位置。寻找 新的位置的方法

双重散列法

双重散列是用于开放寻址法的最好方法之一,所谓双重散列,意思就是不仅要使用一个散列函数。我们 需要使用两个散列函数 , .我们先用第一个散列函数,如果发生冲突,则再利 用第二个散列函数。

线性勘测法

一旦出现冲突后,就尝试后面邻接的位置,直到找到没有被占用的位置为止。但是这种方法虽然解决了 当前的冲突,但可能会造成其他冲突,出现数据聚集现象

int h[N];
// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
int find(int x){int t = (x % N + N) % N;while (h[t] != null && h[t] != x){t ++ ;if (t == N) t = 0;}return t;
}

4(例题)、雪花雪花

有 N 片雪花,每片雪花由六个角组成,每个角都有长度。

第 i 片雪花六个角的长度从某个角开始顺时针依次记为 ai,1,ai,2,…,ai,6。

因为雪花的形状是封闭的环形,所以从任何一个角开始顺时针或逆时针往后记录长度,得到的六元组都代表形状相同的雪花。

例如 ai,1,ai,2,…,ai,6和 ai,2,ai,3,…,ai,6,ai,1 就是形状相同的雪花。

ai,1,ai,2,…,ai,6 和 ai,6,ai,5,…,ai,1 也是形状相同的雪花。

我们称两片雪花形状相同,当且仅当它们各自从某一角开始顺时针或逆时针记录长度,能得到两个相同的六元组。

求这 N 片雪花中是否存在两片形状相同的雪花。

输入格式

第一行输入一个整数 N,代表雪花的数量。

接下来 N 行,每行描述一片雪花。

每行包含 6 个整数,分别代表雪花的六个角的长度(这六个数即为从雪花的随机一个角顺时针或逆时针记录长度得到)。

同行数值之间,用空格隔开。

输出格式

如果不存在两片形状相同的雪花,则输出:

No two snowflakes are alike.

如果存在两片形状相同的雪花,则输出:

Twin snowflakes found.

数据范围

1≤N≤100000,
0≤ai,j<10000000

输入样例:

2
1 2 3 4 5 6
4 3 2 1 6 5

输出样例:

Twin snowflakes found.
难度:简单
时/空限制:1s / 64MB
总通过数:2534
总尝试数:8410
来源:《算法竞赛进阶指南》
算法标签

思路:

#include <iostream>
using namespace std;
const int SIZE = 1e5 + 10, P = 99991;
int snow[SIZE][6], head[SIZE], r[SIZE], a[10];
//snow储存每片雪花6个值;head以雪花Hash值为索引,储存Hash表的表头; r实现链表功能
int n, tot = 0, h;
int Hash(int *a) {  //Hash函数(6个值之和加上6个值之积%P)int sum = 0, mul = 1;for (int i = 0; i < 6; i++) {sum = (sum + a[i]) % P;mul = (long long)mul * a[i] % P;}return (sum + mul) % P;
}
bool find_same_snow(int hh) {for (int i = 0; i < 6; i++) {for (int j = 0; j < 6; j++) {bool flag = true;for (int k = 0; k < 6; k++) if (snow[hh][(i + k) % 6] != a[(j + k) % 6])  flag = false;  //正序判断if (flag) return true;flag = true;for (int k = 0; k < 6; k++) if (snow[hh][(i + k) % 6] != a[(j - k + 6) % 6])  flag = false;  //倒序判断if (flag) return true;}}return false;
}
bool check() {h = Hash(a);         //获取该雪花Hash值for (int i = head[h]; i != 0; i = r[i])if (find_same_snow(i))//将雪花的下标传递给函数return true;   //找到相同雪花tot++;for (int i = 0; i < 6; i++)snow[tot][i] = a[i];//储存雪花值r[tot] = head[h];     //将插入项与原位置后一个连接起来head[h] = tot;            //将头部与插入项连接起来return false;          //未找到相同雪花
}
int main() {cin >> n;for (int i = 1; i <= n; i++) {for (int j = 0; j < 6; j++)cin >> a[j];if (check()) {cout << "Twin snowflakes found." << endl;return 0;}}cout << "No two snowflakes are alike." << endl;return 0;
}

文章目录


字符串Hash O(n)+O(m)

全称字符串前缀哈希法,把字符串变成一个p进制数字(哈希值),实现不同的字符串映射到不同的数字。用来判断一个该字符串是否重复出现过。

对形如 X1X2X3⋯Xn−1Xn 的字符串,采用字符的ascii 码乘上 P 的次方来计算哈希值。

映射公式 (X1×P^n−1+X2×P^n−2+⋯+Xn−1×P^1+Xn×P^0)mod q

一般来说P=131或者13331,q=2^64时最不容易发生冲突

任意字符不可以映射成0,否则会出现不同的字符串都映射成0的情况,比如A,AA,AAA皆为0

问题是比较不同区间的子串是否相同,就转化为对应的哈希值是否相同。
求一个字符串的哈希值就相当于求前缀和,求一个字符串的子串哈希值就相当于求部分和。

1.为了能够任意计算任意范围的hash值需要按照

return h[r]-h[l-1]*p[r-l+1];

区间和公式的理解: ABCDE 与 ABC 的前三个字符值是一样,只差两位,
乘上 P^2 把 ABC 变为 ABC00,再用 ABCDE - ABC00 得到 DE 的哈希值。

小技巧:这里可以将数据类型定义成unsigned long long 类型,产生溢出时相当于自动对 进行取 模,提高效率

代码模板:

#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
typedef unsigned long long ULL;
const int N = 1e5+5,P = 131;//131 13331
ULL h[N],p[N];
char str[N];
// h[i]前i个字符的hash值
// 字符串变成一个p进制数字,体现了字符+顺序,需要确保不同的字符串对应不同的数字
// P = 131 或  13331 Q=2^64,在99%的情况下不会出现冲突
// 使用场景: 两个字符串的子串是否相同
ULL query(int l,int r){return h[r] - h[l-1]*p[r-l+1];
}
int main(){int n,m;cin>>n>>m;cin>>str+1;//下标从1开始//字符串从1开始编号,h[1]为前一个字符的哈希值p[0] = 1;h[0] = 0;for(int i=1;i<=n;i++){p[i] = p[i-1]*P;            //h[i] = h[i-1]*P +str[i];      //前缀和求整个字符串的哈希值}while(m--){int l1,r1,l2,r2;cin>>l1>>r1>>l2>>r2;if(query(l1,r1) == query(l2,r2)) printf("Yes\n");else printf("No\n");}return 0;
}

其中p[0]=1是因为任何一个数的0次方都等于1

1.回文子串的最大长度

如果一个字符串正着读和倒着读是一样的,则称它是回文的。

给定一个长度为 N 的字符串 S,求他的最长回文子串的长度是多少。

输入格式

输入将包含最多 30个测试用例,每个测试用例占一行,以最多 1000000 个小写字符的形式给出。

输入以一个以字符串 END 开头的行表示输入终止。

输出格式

对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。

每个输出占一行。

输入样例:

abcbabcbabcba
abacacbaaaab
END

输出样例:

Case 1: 13
Case 2: 6

代码如下(示例):

小技巧:

在字符之间插入一个#(其它字符),这样字符串长度就变成了奇数,


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const int N = 2000010, base = 131;//长度*2
char str[N];
ull hl[N],hr[N],p[N];
int i,j,k;ull get(ull h[], int l, int r){return h[r]-h[l-1]*p[r-l+1];
}
/*  套用字符串哈希算法。算出正序,逆序哈希。枚举中点,然后二分中点左右两边长度,看其是否相等。
技巧:在字符之间插入一个#(其它字符),这样字符串长度就变成了奇数,
但要注意的是:
算原来字符长度的时候要判断第一个字符是什么,
如果不是#,这样字符长度就是半径+1,否则就是半径。
*/
int main(){int T=1;while(scanf("%s",str+1)){if(str[1]=='E')break;int n=strlen(str+1);for(i=n*2;i;i-=2){str[i]=str[i/2];str[i-1]='a'+26;//在字符之间插入一个其他字符}n*=2;//注意要更新np[0]=1;for(i=1, j=n; i<=n; i++,j-- ){hl[i]=hl[i-1]*base+str[i];//左半边正序哈希hr[i]=hr[i-1]*base+str[j];//右半边逆序哈希p[i]=p[i-1]*base;}int res=0;for(i=1;i<=n;i++){//枚举中点int l=0,r=min(i-1,n-i);//中点i左半径是i-1右半径是n-i,若是回文肯定是取较小者while(l<r){//二分半径int mid=l+r+1>>1;//算hr[]的时候要注意下标对应if (get(hl,i-mid,i-1)!=get(hr,n-(i+mid-1),n-(i+1-1))) //数学计算(看图片解析)注意右边起点和终点的坐标r=mid-1;//mid属于右半边else l=mid;}if(str[i-l]<='z')//若符号比字母多一个,具体计算看图片解析res=max(res,l+1);else//字母比符号多一个res=max(res,l);}printf("Case %d: %d\n",T++,res);}return 0;
}

2 后缀数组

后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者 DC3 算法实现,这超出了我们的讨论范围。

在本题中,我们希望使用快排、Hash 与二分实现一个简单的 O(nlog2n) 的后缀数组求法。

详细地说,给定一个长度为 n 的字符串 S(下标 0∼n−1),我们可以用整数 k(0≤k<n) 表示字符串 S 的后缀 S(k∼n−1)。

把字符串 S 的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。

额外地,我们考虑排名为 i 的后缀与排名为 i−1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。

我们的任务就是求出 SA与 Height 这两个数组。

输入格式

输入一个字符串,其长度不超过 30 万。

字符串由小写字母构成。

输出格式

第一行为数组 SA,相邻两个整数用 1 个空格隔开。

第二行为数组 Height,相邻两个整数用 1 个空格隔开,我们规定 Height[1]=0。

输入样例:

ponoiiipoi

输出样例:

9 4 5 6 2 8 3 1 7 0
0 1 2 1 0 0 2 1 0 2

#include <algorithm>
#include <climits>//INT_MIN(负无穷)这个函数在里面
#include <cstring>
#include <iostream>using namespace std;typedef unsigned long long ULL;const int N = 3e5 + 10, P = 131;int n;
char str[N];
ULL h[N], p[N];
int sa[N];ULL get(int l, int r) {return h[r] - h[l - 1] * p[r - l + 1];
}int get_max_common_prefix(int a, int b) {//a,b对应后缀下标,求相邻后缀的最长公共前缀的长度int l = 0, r = min(n - a + 1, n - b + 1);//n-a+1,n-b+1即是这两个相邻后缀的长度while (l < r) {int mid = (l + r + 1) >> 1;//区间左边是字符串不同,右边相同,mid属于右半边if (get(a, a + mid - 1) != get(b, b + mid - 1)) {r = mid - 1;} else {l = mid;}}return l;
}bool cmp(int a, int b) {//a,b表示下标int l = get_max_common_prefix(a, b);//strint av = a + l > n ? INT_MIN : str[a + l];//表示除了公共前缀后的字符串长度int bv = b + l > n ? INT_MIN : str[b + l];//INT_MIN表示负无穷,eg:i  ipoi的公共子串是i,//i所对应下标是10,10+1>10,即这个短的字符串所有字母就是公共字符串,//除去公共的长度就变为0,所以返回负无穷return av < bv;
}int main() {cin >> (str + 1);n = strlen(str + 1);p[0] = 1;for (int i = 1; i <= n; i++) {h[i] = h[i - 1] * P + str[i];p[i] = p[i - 1] * P;sa[i] = i;//存后缀下标}sort(sa + 1, sa + 1 + n, cmp);for (int i = 1; i <= n; i++) {cout << sa[i] - 1 << " ";}puts("");for (int i = 1; i <= n; i++) {if (i == 1) {//排名为1,i-1为0(不存在),比较不了,所以为0cout << "0 ";} else {cout << get_max_common_prefix(sa[i - 1], sa[i]) << " ";//求相邻两后缀的最长公共前缀的长度}}puts("");return 0;
}

3  矩阵

给定一个 M 行 N 列的 0101 矩阵(只包含数字 0 或 1 的矩阵),再执行 Q 次询问,每次询问给出一个 A 行 B 列的 01 矩阵,求该矩阵是否在原矩阵中出现过。

输入格式

第一行四个整数 M,N,A,B

接下来一个 M 行 N列的 01 矩阵,数字之间没有空格。

接下来一个整数 Q。

接下来 Q 个 A 行 B 列的 01 矩阵,数字之间没有空格。

输出格式

对于每个询问,输出 1 表示出现过,0 表示没有出现过。

数据范围

A≤100,M,N,B≤1000,Q≤1000

输入样例:

3 3 2 2
111
000
111
3
11
00
11
11
00
11

输出样例:

1
0
1

主要想法:对需要判断的大小的矩阵,进行字符串hash。(比如需要在4x4的矩阵里是否存在3x3的相同的矩阵)
可以对4x4的矩阵 进行3x3的字符串hash。
二维字符串hash,可以看成如下图。

每一个方格里的数字表示该方格的数*P^多少次方

1.按列(先卡住列),对每一行进行字符串hash(记为get函数),记为s。
2.当增加了一行,并且行数<=a行数<=a,在原有的ss上, s = s * p[b] + get(hashv[j], l, r),其中s * p[b]相当于把第一行hash抬了一格子,再加上当前这行的hash值get(hashv[j], l, r)
如图,图中是以3x3格子为例子

3.再当前扫描的矩阵行数>= a的时候需要将最上面那一行减去。注意: -get(hashv[j - a], l, r) * p[a * b],*p[a * b]的原因如图,需要将原来的hash值0, 1, 2抬成9, 10, 11。

4.再插入到set的时候,需要判断行数>=a满足要求再插入到set中。

代码如下:(这里用到的unordered_set)在文章最后会讲

#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_set>using namespace std;typedef unsigned long long ULL;const int N = 1010, M = N * N, P = 131;char str[N];
ULL f[N][N], p[M];//f[]表示每一行前缀的hash,即上文说到的hashv[]int n, m, a, b;// 求区间 hash 值
ULL get(ULL f[], int l, int r) {return f[r] - f[l - 1] * p[r - l + 1];
}int main() {cin >> n >> m >> a >> b;// 求取 P 进制数组p[0] = 1;for (int i = 1; i <= m * n; i ++) p[i] = p[i - 1] * P;//预处理m*n的矩阵每一个1*1方块的进制//一般涉及到前缀和的处理,下标都从1开始;若从0开始,s[i-1]会越界for (int i = 1; i <= n; i ++) {scanf("%s", str + 1);//输入每一行// 预先将每一行 hash 化for (int j = 1; j <= m; j ++) f[i][j] = f[i][j - 1] * P + str[j] - '0';}// 枚举每一个 a * b 的矩阵,将 hash 值填入 set 中unordered_set<ULL> hash;for (int i = b; i <= m; i ++) {//按列循环ULL s = 0;int l = i - b + 1, r = i;for (int j = 1; j <= n; j ++) {// 获取从 a 行 移到 a + 1 行的hash 值// s * p^b + hash(a + 1)// s * p^b 是因为 s 保存的是上一行求到的值,它要向下移,//就相当于将上一行和当前行分别看做一个整体,进制为 p^b// + hash(a + 1) 表示将当前行的 hash 值加上s = s * p[b] + get(f[j], l, r);// 将当前以第 a + 1 行为底矩阵的 hash 值 - 以第 a 行为底矩阵第一行的 hash 值if (j > a) s -= get(f[j - a], l, r) * p[a * b];// 将该矩阵的 hash 值放入 setif (j >= a) hash.insert(s);}}int k;cin >> k;while (k --) {ULL s = 0;for (int i = 0; i < a; i ++) {scanf("%s", str);// 求出当前行的 hash 值for (int j = 0; j < b; j ++) s = s * P + str[j] - '0';}// 判断 set 中是否存在该矩阵if (hash.count(s)) puts("1");else puts("0");}return 0;
}

注意:一般涉及到前缀和的处理,下标都从1开始;若从0开始,s[i-1]会越界

4 树形地铁系统(涉及树的知识)

一些主要城市拥有树形的地铁系统,即在任何一对车站之间,有且只有一种方式可以乘坐地铁。

此外,这些城市大多数都有一个中央车站。

想象一下,你是一名在拥有树形地铁系统的城市游玩的游客,你想探索该城市完整的地铁线路。

你从中央车站出发,随机选择一条地铁线,然后乘坐地铁行进。

每次到达一个车站,你都将选择一条尚未乘坐过的地铁线路进行乘坐。

如果不存在未乘坐过的线路,则退回到上一个车站,再做选择。

直到你将所有地铁线路都乘坐过两次(往返各一次),此时你将回到中央车站。

之后,你以一种特殊的方式回忆自己的坐车过程,你将你的完整地铁乘坐路线编码为一个二进制字符串。

其中 0 编码表示你乘坐地铁线路到达距离中央车站更远的一站,1编码表示你乘坐地铁线路到达距离中央车站更近的一站。

输入格式

第一行输入一个正整数 n,代表测试用例数量。

每个测试用例由两行组成,每行输入一个由字符 0 和 1 构成的字符串,长度最多为 3000, 两个字符串都描述了一种树形地铁系统的正确探索路线。

输出格式

对于每个测试用例,如果两个字符串描述的探索路线可以视为同一个地铁系统的两种探索路线,则输出 same

否则,输出 different

每行输出一个结果。

输入样例:

2
0010011101001011
0100011011001011
0100101100100111
0011000111010101

输出样例:

same
different

性质:如果两颗树是同构的,当且仅当 这两颗树的最小表示是相同的

给定两棵树的 DFS 过程(从根节点出发,遇到0就向下走一步,
遇到1就向上走一步),判断它们是否同构。
可以对树的每个子树,都进行字典序的排序,逐层递归。
最后如果两棵树同构的话,那么只有可能是排列上的不同,
即可以通过最小表示,对每棵树生成唯一的最小表示。
而结构相同时,DFS 过程完全相同,最小表示显然相同

代码如下:


#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int T;
string dfs(string &seq,int &u){u++;vector <string> seqs;while(seq[u]=='0') seqs.push_back(dfs(seq,u));u++;//当str[u] == 1的时候需要抛弃回去的那条边,所以u ++;sort(seqs.begin(),seqs.end());string res="0";for(auto &s:seqs) res+=s;res+='1';//因为每次回去抛弃了那条边,所以得补上,上面的1return res;
}
int main(){cin>>T;while(T--){string a,b;cin>>a>>b;a='0'+a+'1';b='0'+b+'1';int ua=0,ub=0;if(dfs(a,ua)==dfs(b,ub))   puts("same");else puts("different");}return 0;
}

三 C++ ST库之 unordered_map——哈希表

unordered_map是C++中的哈希表,可以在任意类型与类型之间做映射。

基本操作
1 引用头文件(C++11):#include <unordered_map>
2 定义:unordered_map<int,int>、unordered_map<string, double> ...
3 插入:例如将("ABC" -> 5.45) 插入unordered_map<string, double> hash中,hash["ABC"]=5.45
4 查询:hash["ABC"]会返回5.45
5 判断key是否存在:hash.count("ABC") != 0 或 hash.find("ABC") != hash.end()
6 遍历

for (auto &item : hash)
{cout << item.first << ' ' << item.second << endl;
}

for (unordered_map<string, double>::iterator it = hash.begin(); it != hash.end(); it ++ )
{cout << it->first << ' ' << it->second << endl;
}

进阶操作
如果想让自定义的class作为key(unordered_map<key,value>)来使用unordered_map,需要实现:
(1) 哈希函数,需要实现一个class重载operator(),将自定义class变量映射到一个size_t类型的数。一般常用std::hash模板来实现。
(2) 判断两个自定义class类型的变量是否相等的函数,一般在自定义class里重载operator==。
示例代码:

#include <iostream>
#include <vector>
#include <unordered_map>using namespace std;class Myclass
{
public:int first;vector<int> second;// 重载等号,判断两个Myclass类型的变量是否相等bool operator== (const Myclass &other) const{return first == other.first && second == other.second;}
};// 实现Myclass类的hash函数
namespace std
{template <>struct hash<Myclass>{size_t operator()(const Myclass &k) const{int h = k.first;for (auto x : k.second){h ^= x;}return h;}};
}int main()
{unordered_map<Myclass, double> S;Myclass a = { 2, {3, 4} };Myclass b = { 3, {1, 2, 3, 4} };S[a] = 2.5;S[b] = 3.123;cout << S[a] << ' ' << S[b] << endl;return 0;
}

输出:

2.5 3.123

参考《算法竞赛进阶指南》和网站Acwing

Hash(哈希(字符串哈希))模板和做题总结(详细易懂)相关推荐

  1. 字符串查找函数Strstr函数的实现(详细易懂)

    首先,字符串查找函数是在目的字符串中查找源字符串的首次出现的具体位置,若找到了便返回该位置的地址,若没有找到,则返回空指针NULL char* strstr(const char*arr1 , con ...

  2. ~~字符串哈希(数据结构)(附模板题AcWing 841 字符串哈希)

    核心思想: 将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低. 小技巧: 取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果. ...

  3. 【算法学习】字符串哈希(Hash)

    什么是字符串Hash 构造字符串Hash 1)自然溢出方法 2)单Hash方法 3)双Hash方法 4)三种不同的构造方法的对比 获取子串的Hash O(1) 1)例子 2)公式 具体的题目例子 1) ...

  4. 字符串哈希(魔咒词典hdu1880)

    魔咒词典(字符串哈希) 哈利波特在魔法学校的必修课之一就是学习魔咒.据说魔法世界有100000种不同的魔咒,哈利很难全部记住,但是为了对抗强敌,他必须在危急时刻能够调用任何一个需要的魔咒,所以他需要你 ...

  5. ELFhash - 优秀的字符串哈希算法

    原 ELFhash - 优秀的字符串哈希算法 分类:算法杂论算法精讲数据结构 (1424)  (2) 1.字符串哈希: 我们先从字符串哈希说起 在很多的情况下,我们有可能会获得大量的字符串,每个字符串 ...

  6. 相同的字符串哈希值一样吗_关于哈希,来看这里!

    我想跟着大家一起重新学习下关于哈希的一切--哈希.哈希函数.哈希表. 这三者有什么样的爱恨情仇? 为什么Object类中需要有一个hashCode()方法?它跟equals()方法有什么关系? 如何编 ...

  7. Android程序获得APP哈希值,Android – SMS Retriever API – 计算应用程序的哈希字符串问题...

    我是Android的新手,我正在尝试实现SMS Retriever API,以便在我的应用中使用OTP. 不幸的是,我陷入了"计算你的应用程序的哈希字符串"的部分 我在这里引用了指 ...

  8. 中石油训练赛 - DNA(字符串哈希)

    题目链接:点击查看 题目大意:给出一串只由A,C,G,T组成的字符串,再给出一个数字k,问每个长度为k的连续子串,出现的次数最多是多少次 题目分析:O(n)哈希一下,O(n)更新一下用无序map维护的 ...

  9. HDU - 3613 Best Reward(字符串哈希)

    题目链接:点击查看 题目大意:给出一个字符串,每个字母都有一个贡献值,现在要将这个字符串拆成两个子串,如果子串是回文串的话,贡献就是其中每个字母的贡献和,现在问贡献最大为多少 题目分析:很简单的一道回 ...

最新文章

  1. MPB:林科院袁志林组-​栎类外生菌根形态学特征描述
  2. 使用主机telnet远程管理路由器和交换机的详细过程及截图
  3. PIE_SDK.NET功能表
  4. c语言用define预处理命令定义,C语言程序设计第八章预处理命令..doc
  5. es6语法-箭头函数
  6. procyon java8_java jdk 8反编译工具JD-GUI、procyon-decompiler、luyten、crf下载使用简介
  7. 程序员工资高,但为什么越来越多的人都不再愿意做程序员呢?
  8. 语料库python_NLPPython笔记——语料库
  9. 关于在XP操作系统和IIS5.1环境下的MVC环境搭建之IIS错误
  10. Lambda表达式公共拼接函数(原创)
  11. centos7 rpm安装时报警 Header V3 RSA/SHA256 Signature, key ID 352c64e5: NOKEY
  12. IonIcons图标大全
  13. 二、概率p值检验例题(R语言)
  14. tp摄像头的默认地址_tplink的ip默认地址是什么?
  15. 河北等保测评机构项目测评收费价格标准参考
  16. 万用表怎么测电池内阻_万用表怎么测量电阻
  17. stream_kws_cnn
  18. 更新至OSX 10.10后MBA外接网卡无法使用的解决
  19. win10忘记redis密码
  20. Python教程(十)--if 实例运用(棒子老虎鸡游戏)

热门文章

  1. 自监督直接和具体任务的结合(Task Related Self-Supervised Learning)的探索
  2. HDU - 1164 - Eddy‘s research I
  3. 新媒体行业拓客的10个经典方法
  4. Windows7 U盾无法使用解决方法(农行、建行、工行、光大、广发、交行)
  5. 水池水位无线自动控制系统
  6. PS-文字如何竖排版
  7. 接受电话面试时有哪些要注意的
  8. 逃离同质化,OPPO折叠屏正在笃定远一点的未来
  9. 贝加莱驱动器电源模块维修8B0C0320HW00.002-1
  10. Python数据分析:股票数据分析案例