蒟蒻今天新学了字符串hash
在学习之前,就听闻此算法为瞎搞算法之一
这不禁让我好奇心爆炸
于是我轻轻撩开了她的面纱
众所周知,字符串的题都无比恶心
hash就是为了把字符串变成整数而量身定制的
比如一个字符串abcd
我们可以让它变为131进制数
(至于为什么是131(或31,1331),这就是集结了大佬们的智慧,可以将它的冲突率降低)
在操作时,每一个前缀用一个数去表示,当有新的字母加入,就将其*131再加上后面的字母所对应的数字
a[i]=a[i-1]*131+s[i]-'a'+1;
这就是hash的简单操作
#include<bits/stdc++.h>
typedef unsigned long long Ull;///ull可以在溢出时自己取余
using namespace std;
char s[10005];
int base=131;///常用131/13331以降低哈希冲突概率
Ull a[10005];
int main()
{
scanf("%s",s+1);
int l=strlen(s+1);
printf("%d\n",l);
for(int i=1;i<=l;i++)
{
a[i]=a[i-1]*base+s[i]-'a'+1;
printf("%llu\n",a[i]);
}
return 0;
}
另外的,对于一个字符串,如果需要取其一部分字串的hash值,就有
以上图为例,假如说有一个字符串 “abcdefg”
字串ab的hash值存在a[b]中,但是在插入后面若干个(r-l+1)
字母之后,ab被向前推了一段距离
每推一次都乘了131
所以就有了下面的式子(数位板太舒服了QWQ)
虽然里面含有一个幂运算,但是我们可以另外开一个数组,专门存131的i次方,需要的时候直接下标取用就ok
基础的hash就这么多了,先占个坑,等本蒟蒻补完题了再更hash的用法QWQ
———————————————————————————
2020.3.14更新
最近补了几道hash题,正好把坑填上。
简单的操作前面都说过了
有一道题是要求回文子串的最大长度
听大佬说有个专门算他的算法叫马拉车。。。
这坑以后再填
这道题所用的算法就是二分+hash
有一个比较有意思的处理方式
比如说字符串abba与abcba,长度一个是奇数一个是偶数
我们为了降低编程难度,就可以在每两个字母之间加上‘#’
使其长度都变成奇数
int l=strlen(s+1);///p.s.字符串头一定要存在第一位
for(int i=2*l;i>0;i-=2)///一定要从后往前
{
s[i]=s[i/2];
s[i-1]='#';
}
//这样在后续操作中就可以只考虑l为奇数的情况了
之后就是要有两个hash数组
一个存正向,一个存反向
顺便把131的pow数组p[]循环出来
p[0]=1;
for(int i=1, j=ll; i<=ll; i++,j--)
{
hashl[i]=hashl[i-1]*131+s[i]-'a'+1;
hashr[i]=hashr[i-1]*131+s[j]-'a'+1;//注意这里在计算反字符串时也要正着计数而不是倒着计数
p[i]=p[i-1]*131;
}
接下来便是枚举中点+二分半径
在这挖个二分的坑,待我总结一下优质的板子(OVO)
///geth是前面写的用来计算hash值的函数
for(int i=1; i<=ll; i++ )
{
int l=0;///这里一定要初始化l=0
int r=min(i-1,ll-i);///半径
while(l<r)
{
int mid=(l+r+1)/2;
if(geth(hashl,i-mid,i-1)!=geth(hashr,ll-(mid+i)+1,ll-(i+1)+1))///这里需要仔细推导一下
{
/// printf("%c %c %c %c %llu %llu\n",s[i-mid],s[i-1],s[ll-(mid+i)+1],s[ll-(i+1)+1],geth(hashl,i-mid,i-1),geth(hashr,ll-(mid+i)+1,ll-(i+1)+1));
r=mid-1;
}
else l=mid;
///printf("%d ",l);
}
if(s[i-l]=='#')
{
maxx=max(maxx,l);
}
else maxx=max(maxx,l+1);
}
这道题主要部分已经解决了,后面附上ac源码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int maxn=2000000+10;
char s[maxn];
ULL base=131;
ULL p[maxn];
ULL hashl[maxn];
ULL hashr[maxn];
ULL geth(ULL *h,int l,int r)
{
return h[r]-h[l-1]*p[r-l+1];
}
int main()
{
int cnt=1;
while(~scanf("%s",s+1),strcmp("END",s+1))
{
int lon=strlen(s+1);
int ll=2*lon;
for(int i=ll; i>0; i-=2)
{
s[i]=s[i/2];
s[i-1]='#';
}
p[0]=1;
for(int i=1, j=ll; i<=ll; i++,j--)
{
hashl[i]=hashl[i-1]*131+s[i]-'a'+1;
hashr[i]=hashr[i-1]*131+s[j]-'a'+1;
p[i]=p[i-1]*131;
}
int maxx=0;
for(int i=1; i<=ll; i++ )
{
int l=0;
int r=min(i-1,ll-i);
while(l<r)
{
int mid=(l+r+1)/2;
if(geth(hashl,i-mid,i-1)!=geth(hashr,ll-(mid+i)+1,ll-(i+1)+1))
{
/// printf("%c %c %c %c %llu %llu\n",s[i-mid],s[i-1],s[ll-(mid+i)+1],s[ll-(i+1)+1],geth(hashl,i-mid,i-1),geth(hashr,ll-(mid+i)+1,ll-(i+1)+1));
r=mid-1;
}
else l=mid;
///printf("%d ",l);
}
if(s[i-l]=='#')
{
maxx=max(maxx,l);
}
else maxx=max(maxx,l+1);
}
printf("Case %d: %d\n",cnt++,maxx);
}
return 0;
}
///(题目来源:ACwing139)
对于hash更多的应用以及马拉车算法,先挖坑QWQ