hash 复习

众所周知,这是字符串问题最好的做法。

定义

考虑一个字符串 \(s\) 的哈希,\(f(s)=\sum^l_{i=1} s[i]\times b^{l-i}\bmod M\)

这个东西需要认识到,下标越大的字符权值越小,这是模仿 进制 的做法。下文基于这样的哈希方法。

\(b\) 可以任取,\(M\) 是一个大数。

  • 哈希冲突

存在 \(s\neq s'\),使得 \(f(s)=f(s')\)

解决方案:

  • 首先,OI 赛制不太卡自然溢出。完全可以通过 unsigned long long 的方法做到 \(M=2^{64}\)

  • 如果真的不放心,可以通过对两个大素数分别取模(例如 \(P_1=998244353\)\(P_2=10^9+7\)),必须两种哈希的值分别相等,我们才认定 \(s=s'\),这样的 \(M\) 相当于 \(P_1\times P_2\)其实没必要。

  • 子串哈希

对于一个字符串 \(s\) ,我们记录下其每一个前缀的哈希值,也就是 \(f_i(s)\) 表示 \(f(s[1\cdots i])\) 的哈希值。

然后我们发现 \(f(s[l \cdots r])=f_r(s)-f_{l-1}s \times b^{r-l+1}\)

  • 字符串匹配

计算出文本串每个长度为模式串长度的子串的哈希值,与模式串比较即可。这个算法不劣于 KMP,时间复杂度也是 \(O(m+n)\) 的。

  • 允许 \(k\) 次失配的字符串匹配

设当前考虑的子串是 \(s'\),这样的子串至多只有 \(n\) 个。

考虑每次通过哈希+二分找 \(s'\) 和模式串第一个不同的位置,将 \(s'\) 和模式串适配位置之前的部分删掉,然后继续找下一个失配的位置,最多失配 \(k\) 次。

时间复杂度 \(O(m+kn\log m)\)

  • 最长回文子串

枚举回文中心 \(r\),二分最长的 \(len\) 使得,\(Hash_{[r-len+1,r]}=Hash_{[r,r+len-1]}\)。需要预处理正着的哈希和倒着的哈希。

时间复杂度 \(O(n\log n)\)

\(f_i\) 表示以 \(i\) 结尾的最长回文串长度,答案就是 \(\max f_i\)。考虑到 \(f_i \le f_{i-1}+2\)。我们暴力从 \(f_{i-1}+2\) 开始递减。

实际暴力次数不超过 \(2n\) 次,复杂度为 \(O(n)\)

  • 最长公共子串

二分长度为 \(k\) 的公共子串是否存在。将每一个字符串长度为 \(k\) 的子串扔到哈希里,然后判断是否 \(n\) 个字符串都含有这个哈希即可。

时间复杂度 \(O(m+n\log n)\)

posted @ 2024-02-29 15:01  wtcqwq  阅读(25)  评论(0)    收藏  举报