写给萌新的字符串hash算法,语言不严谨就算了,固然也欢迎dalao指点QAQ算法
$hash$是一种映射,在信息学中能够用于将一些不方便做为下标储存的结构看成一个数来存起来,方便$O$(1)的查找,可能不太好用,可是思惟极其重要优化
模板:求两个字符串之间是否存在包含关系spa
KMP模板题a字符串
例如$bc$和$cbca$这两个串,$bc$在$cbca$中出现过,它们存在包含关系,那咱们是怎么看出来的呢hash
试着模拟一下寻找过程:$bc$串长度小一些,必定是用它来作模式串(字串)。从文本串(母串)中每次找一个长度为2的短串,看是否是和模式串同样的,一直找到结尾,在这里的$cbca$就分别有三个短串($cb$、$bc$、$ca$),咱们发现了第二次的$bc$和模式串同样,因此就判断出来找到了。模板
若是常规来作,时间为$O$(母串中短串个数*字串长度),约为$O$(mn)程序
首先枚举短串这一步无法优化了(要是有就不是$hash$了),因此主要优化找到短串后怎么$O$(1)的和模式串判断相不相同数据
$hash$的特征就是不一样的$key$(就是目标位置)对应的数据不一样,因此将字符串转化为数字应该注意一一对应,避免哈希冲突(好比不一样字符串对应了同一个值,可是你的程序仍是会判断它们是同一个字符串)语言
通常的字符串$hash$值求法(终于到正题了)时间
给一个字符串
从左到右枚举字符串的每一位,每个字母直接对应它的ASCII码(就变成$int$了),对应好了就把每一位加起来,就愉快的冲突了
直接相加会冲突,例如$ab$和$ba$,第二串后来的那个$a$和第一串的前面的$a$虽然一个更老一个更年轻,可是它们的做用竟然是同样的,这是不符合常识的(我是在说实话)
因此,为了使资质更老的$a$更显眼,能够在处理以后的那些后代的时候给它乘上一个数$base$显示它的不一样。若是考虑到每个字符后面都有后代的话,那么每处理一个后面的字符,前面的祖宗们就都会乘上一个数。容易看出,每个位置都比它后面那个位置多乘了一次,这样就能够显示出各个位置的等级差距了2333
再结合以前的直接相加,就能够表示出来每个不一样的字符串了,即:
val ["abc"] = 'a'$$base^2+'b'$$base^1+'c'$*$base^0
那么对于一个母串,怎么提取它[ $l$ , $r$ ]中的$hash$值呢。咱们已经知道了这个串从1到每一个位置这一部分的$hash$值,这相似于前缀和,即$hash$[ $r$ ]-$hash$[ $l$-1 ],可是因为对于$r$位置的$hash$[ $r$ ],它前面一部分(即被它包含在内的$hash$[ $l$ ]部分)被多乘了许屡次$base$,减的时候应该给$hash$[ $l$ ]也乘上
(换个说法:求出$hash$[ $l$-1 ]以后,继续向后面走,每走一步都会$hash$[ $l$-1 ]乘上$base$,一直求到$hash$[ $r$ ]时已经乘了($r$-$l$+1)个$base$了,实际上
hash[ r ]=hash[ l,r ]+hash[ l-1 ]$*$base^(r-l+1))
因此答案应该是(多乘了的次数即[ $l$,$r$ ]区间长度)
val[ l , r ]=hash[ r ]-hash[ l-1 ]$*$base^(r-l+1)
最后,由于乘的$base$通常很大,因此乘多了容易爆,要取模,为了不麻烦,通常使用$unsigned$ $long$ $long$
Q:hash如何支持单点修改?
A:能够用线段树维护
要用线段树维护要资瓷区间合并->
hash=左子树hash*(base^右子树size)+右子树hash