公式证明
在之前的某一篇博客中我写了,关于BDKRHash的简短的介绍,还没有对其公式和原理进行阐述,实在是罪过罪过。
首先给出BDKRHash的公式
1.$$hashvalue[i]=(hashvalue[i-1]*seed+str[i]-'a'+1)%P$$(也可以用ASCLL码值,但容易爆)
2.$$Hashvalue[l……r]=(hashvalue[r]-hashvlaue[l-1]*seed^{r-l+1})%P$$(注意第一个Hashvalue与hashvalue不同)
第一个公式不需要证明,这是BDKRHash的定义式,意思就是:发明BDKRHash的人就是这么写的。这个东西只有用记忆的方法来掌握,如果要深究其原理,太过深奥,蒟蒻我不懂。
接下来我们针对第二个公式进行证明: Hashvalue代表的是从l到r这个区间的字串的hash值,hashvalue代表从第1个开始到第i个的hash值。下面开始证明
先看第一个公式
$$hashvalue[i]=(hashvalue[i-1]*seed+str[i]-'a'+1)%P$$
我们把每一项展开可以得到(为方便表达,接下来hashvalue简写为hash,str[i]-'a'+1简写为str[i])
$$hash[0]=0$$
$$hash[1]=str[1]%P$$
$$hash[2]=hash[1]*seed+str[2]$$
$$hash[3]=hash[2]*seed+str[3]$$
$$hash[i]=hash[i-1]*seed+str[i]$$
$$hash[n]=hash[n-1]*seed+str[n]$$
看出一些规律了吗?如果没有,我们再把每一项中的hash[i]*seed展开:
$$hash[2]=(str[1])*seed+str[2]$$
$$hash[3]=(str[1]*seed+str[2])*seed+str[3]$$
$$hash[4]=((str[1]*seed+str[2])*seed+str[3])*seed+str[4]$$
$$hash[i]=(((str[1]*seed+str[2])*seed+str[3])*seed······)*seed+str[i-1])+str[i]$$
再把括号全部展开:
$$hash[i]=str[1]*seed^{i-1}+str[2]*seed^{i-2}+······+str[i]*seed^{i-i=0}$$
到这里我们已经大致得到了BDKRHash的最终表达式,通过对这个式子进行变形,就可以得到第二个公式:
$$令l=1,则r=r-l+1$$
$$hash[l······r]=hash[1······r-l+1]=str[1]*seed^{r-l}+str[2]*seed^{r-l-1}+······+str[r-l+1]*seed$$
把1还原为l
$$hash[l······r]=str[l]*seed^{r-l}+str[l+1]*seed^{r-l-1}+······+str[r]*seed$$
写到这里,可能大家都已经明白了。
$$hash[1······l-1]=str[1]*seed^{l-2}+str[2]*seed^{l-3}+······+str[l-1]*seed\dashrightarrow①$$
$$hash[1······r]=str[1]*seed^{r-1}+str[2]*seed^{r-2}+······+str[r]*seed\dashrightarrow②$$
$$②-①*seed^{r-l+1}$$
得到 $$hash[1······r]=hash[1······l-1]*seed^{r-l+1}+str[l]*seed^{r-l}+str[l+1]*seed^{r-l-1}+······+str[r]*seed$$
$$hash[l······r]=str[l]*seed^{r-l}+str[l+1]*seed^{r-l-1}+······+str[r]*seed得$$
$$hash[l······r]=hash[1······r]-hash[1······l-1]*seed^{r-l+1}$$
又Hashvalue代表的是从l到r这个区间的字串的hash值,hashvalue代表从第1个开始到第i个的hash值。
$$Hashvalue[l……r]=(hashvalue[r]-hashvlaue[l-1]*seed^{r-l+1})%P$$
完结。 如果不懂,可以自己按着推一遍。
BDKRHash判断回文字符串:
主要是通过下面这个式子判断。
$$hash[i]=str[1]*seed^{i-1}+str[2]*seed^{i-2}+······+str[i]*seed$$
观察之后不难发现,hash值是可以逆向来求的,即:
int seed=1; for(int i=n;i>=1;i--) { hash[i]=hash[i+1]+str[i]*seed; seed*=107; }
所以我们只用循环一遍就可以得到,1到i长度的字符串是不是回文字符串。