哈希算法

最近参加了集训,学了很多新的算法。所以8月20号左右会开始写博客(才不是因为接下来几天要出去旅行

 

今天学习了字符串。大概学习了:哈希(Hash),KMP,Trie树,Trie图,AC自动机,后缀数组……(其实后面几个没太听懂……)

准备着先写写模板。但是发现:哈希网上的模板大多数都是对于一整个字符串make_hash。

那哈希的一个很大的优点——在 ?(1) 的时间内求出字符串的任意一个子串的哈希值,不就很不方便了吗?

所以自己苦思冥想(大量翻阅其他的文献)加上询问巨佬队友,总算写出了类似于前缀和的哈希!!(不过后来发现网上也有……%%%

对于字符串 S,我们常用的哈希方式是将它转化为一个整数:???=?[1]∗?n-1+…+?[?] (我比较喜欢字符数组下标从1开始)

其中 x 是我们选取的一个底数(一般 res 需要对一个质数取模)。

至于自然溢出。。。现在已经构造出能够卡掉所有哈希底数的自然溢出的字符串了 (他死了)

而重点来了:在一般做题时,我们会设 f[i] 表示前缀 i 的哈希值。

 这样就能让我们在 ?(1) 的时间内求出字符串的任意一个子串的哈希值(要先预处理了底数的幂) 

   ?[?…?]=?[?]−?[?−1]∗?^(?−?+1)。

哈希算法真的非常优秀,常见用法:

1、由于哈希算法非常玄学,OI 中鲜有出现以哈希为官方标解的算法。

      不过哈希往往能作为一个非常优秀的暴力。特别是与 map 等 stl 相结合时能非常方便地维护字符串的集合。

2、当然大家也可以使用“挂链”的方法维护多个字符串的哈希。。。大致是将 res 再对一个较小的数取模,将其二次映射到那个较小的值上。

3、同时,基于生日悖论,在有 √??? 级别的字符串时哈希就变得非常不可靠。此时可以通过取两个模数的方法来优化正确率。

   这里又涉及到一个叫“孪生素数”的东西(我是蒟蒻我不太会>_<)反正大家做题就取 109+7 和 109+9 就好了。。。

4、哈希算法还常常与二分相结合,用于求两个(或多个)字符串的最长公共 前/后 缀。

5、可以在某些时候代替后缀数组,而且这东西的常数非常优秀,有时甚至能让你达成暴力踩标算的成就 (会多一个log,但是常数真的很优秀)

接下来是代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int INF=0x3f3f3f3f;
 5 const ll mod=212370440130137957ll;     // 大质数
 6 const ll base=131;                     // 底数,一般是131或233;
 7 const int Maxn=1e6+5;
 8 
 9 ll ba[Maxn];
10 ll ha[Maxn],hb[Maxn];
11 char a[Maxn] ,b[Maxn];
12 
13 int main(){
14     scanf("%s%s",a+1,b+1);
15     int lena=strlen(a+1);
16     int lenb=strlen(b+1);
17     ha[0]=0; hb[0]=0; ba[0]=1;                       // 初始化!!
18     for(int i=1;i<=max(lena,lenb);i++) ba[i]=ba[i-1]*base;      // 预处理 base^1~base^n; 
19     for(int i=1;i<=lena;i++) ha[i]=(ha[i-1]*base+(ll)(a[i]))%mod;  //make_hash;
20     for(int i=1;i<=lenb;i++) hb[i]=(hb[i-1]*base+(ll)(b[i]))%mod;
21     printf("%lld\n",ha[4]);
22     printf("%lld\n",hb[5]-hb[2-1]*ba[5-2+1]);              // hash[l...r] = hash[r]-hash[l-1]*x[r-l+1];
23     return 0;
24 }
25 /*
26 输入:
27 abcdhhhhhh
28 habcdhhhhh
29 输出:
30 219759674
31 219759674
32  */

 

转载于:https://www.cnblogs.com/pengcheng-official/p/11332642.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值