字符串hash(赖皮算法) M形字符串

字符串hash

• Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做
预映射, pre-image),通过哈希算法,变换成固定长度的输出,该输出就是哈希值。
• 哈希值的空间通常远小于输入的空间,不同的输入可能会哈希成相同的输出,所以不可能从
哈希值来确定唯一的输入值。
• 字符串hash —— 把一个字符串变成一个数字(一般是小于int的)

说人话

就是把一个字符串对应成一个数字,如果两个字符串相等那么它们对应的数字一定相等,对应的数字相等这两个字符串不一定相等,但是,我们认为它们是相等的。
如果被卡了,考虑求一个字符串的两个hash值,如果两个字符串的两个hash
值都相等,那么这两个字符串就相等。
如果还被卡了,换一下模的那个数。

如何把一个字符串变成一个数?

看这个例子, 字符串:abc,
• *公式:hash[i]=(hash[i-1]p+idx(s[i]))%mod
• 字符串下标从0开始, idx(a)=1, idx(b)=2, idx©=3,idx(d)=4;
• 取p=13 ,mod=101
• hash[0]=1,表示 a 映射为1
• hash[1]=(hash[0]*p+idx (b))%mod=15,ab 映射为 15
• hash[2]=(hash[1]*p+idx ( c))%mod=97
• 这样,我们就把 abc 映射为 97 这个数字了

另外对于一个字符串的子串,比如s[l] ~ s[r],这个串的hash值这样子求
hash[l…r]=(hash[r]-hash[l-1]*(p^ (r-l+1))) % mod

双hash

• hash1[i]=(hash1[i-1]*p+idx(s[i]))%mod1
• hash2[i]=(hash2[i-1]*p+idx(s[i]))%mod2
• mod1一般取1e9+7,mod2一般取1e9+9为什么这么取?
• 1000000007和1000000009是一对孪生素数,取它们,冲突的概率极低

eg. M形字符串

在这里插入图片描述

在这里插入图片描述
分析:
利用hash判断一个串是否回文,做法是判断这个串的正hash是否等于逆hash,等于则回文。
而对于题目中所说的M型字符串,首先这个字符串本身就是回文的,然后,这个字符串的左半串和右半串也是回文的
AC代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const ll mod = 1e9 + 7;
int len, ans;
ll pre[200050];//正hash
ll bac[200050];//逆hash
ll base[200050];//记录26的i次方 mod 1e9 + 7(变成26进制数,因为字母一共26个)
string s;

int idx(char ch)
{
    return (int)(ch - 'a' + 1);
}

ll getpre(int l, int r)//求子串s[l...r]的正hash
{
    return (pre[r] - pre[l - 1] * base[r - l + 1] + mod) % mod;
}

ll getback(int l, int r)//求子串是s[l...r]的逆hash
{
    return (bac[r] - bac[l - 1] * base[r - l + 1] + mod) % mod;
}

bool judge(int i, int j)//判断子串s[l...r]是否回文
{
    return getpre(i, j) == getback(len - j + 1, len - i + 1);
}

int half(int x)
{
    return (x + 1) / 2;
}

int main()
{
    cin >> s;
    s = " " + s;
    len = s.size();
    base[0] = 1;
    for(int i = 1; i <= len; ++i)
    {
        base[i] = base[i - 1] * 26 % mod;
        pre[i] = (pre[i - 1] * 26 + idx(s[i])) % mod;
        bac[i] = (bac[i - 1] * 26 + idx(s[len - i + 1]))  % mod;
    }
    for(int i = 1; i <= len; ++i)
    {
        //s[1...i]回文,且其左半也是回文的(左半回文,整体回文,右半一定也回文)
        if(judge(1, i) && judge(1, half(i)))    ans++;
    }
    printf("%d\n", ans);
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值