蒟蒻的瞎搞哈希

蒟蒻今天新学了字符串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值,就有字丑QWQ
以上图为例,假如说有一个字符串 “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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值