字符串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;
}