字符串哈希

哈希又称散列,是把一个对象通过哈希函数映射成一个整数值(哈希值)

设字符串为s,s[i]表示i位置字符的ASCII码,hash[i]表示字符串s的子串,前缀i的哈希值。
字符串哈希的常用哈希函数为:
hash[i]=hash[i1]p+s[i]hash[i]=hash[i-1]*p+s[i]
(p通常为质数,可以进一步减少哈希冲突)也可写作:
hash[i]=i=0l1s[i]pihash[i]=\sum_{i=0}^{l-1}s[i]*p^i

这个哈希函数对子串的运算非常友好,有下面一个重要性质:

  • 先按照公式2展开,变得更加直观

字符串哈希
显然,如果要求字符串s的子串s3s4的哈希值。由公式2可知s3s4的哈希值应该是s3*p+s4,显然不能直接用hash[4]-hash[2]得到s3s4的哈希值,而是应当使用hash[4]-hash[2]*p2
所以可以推理出求子串s[l,r]哈希值公式应该为:
hash[l,r]=hash[r]hash[l]prl+1hash[l,r]=hash[r]-hash[l]*p^{r-l+1}
这可以用于字符串的模式匹配,例如:

给定一个模式串t,一个带匹配串s,问带匹配串s是否包含串t作为子串。

由公式2可知,模式串t的哈希值
hash[i]=i=0l1s[i]pihash[i]=\sum_{i=0}^{l-1}s[i]*p^i
这是一个可以被计算出来的固定值,再把它与待匹配串s等长的所有子串的哈希值进行比较。
那么如何计算待匹配串s等长的所有子串的哈希值呢?
也非常简单,可以直接根据公式2求,也可以根据公式3如下推导
hash[l+1,r+1]=hash[r+1]hash[l+1]prl+1hash[l+1,r+1]=hash[r+1]-hash[l+1]*p^{r-l+1}
通过公式3展开后,再用公式3分别展开(看上面的图就明白了)
hash[r+1]就相当于hash[r]*p+s[r+1]
=(hash[r]p+s[r+1])(hash[l]p+s[l+1])prl+1=(hash[r]*p+s[r+1])-(hash[l]*p+s[l+1])*p^{r-l+1}
然后合并,括号内可以凑出逆向公式3式子
=p(hash[r]hash[l]prl+1)+s[r+1]s[l+1]prl+1=p*(hash[r]-hash[l]*p^{r-l+1})+s[r+1]-s[l+1]*p^{r-l+1}
=phash[l][r]+s[r+1]s[l+1]prl+1=p*hash[l][r]+s[r+1]-s[l+1]*p^{r-l+1}

最后结论:
hash[l+1,r+1]=phash[l][r]+s[r+1]s[l+1]prl+1hash[l+1,r+1]=p*hash[l][r]+s[r+1]-s[l+1]*p^{r-l+1}
这意味着子串[l+1,r+1]的哈希值可以直接由子串[l,r]与s[l+1]、s[r+1]算出来,复杂度是O(1),而不需要通过公式2遍历一次s的子串。
这样即使把所有子串全部算出来也只有O(n)的复杂度。

参考:https://blog.csdn.net/pengwill97/article/details/80879387

相关文章: