求哈希值
根据hash函数来求hash值:hash[i] = hash[i-1]*p+val[i],其中p为素数,一般取233,hash[i]表示1-i这一段的hash值,val[i]为第i个字符的值。
下面以数字字符为例求hash值。
一般采用unsigned long long,会自动取模。
code
h1[0] = 0;//正向hash值
h2[0] = 0;//反向hash值
p[0] = 1;
for(int i = 1; i <= n; i++){
h1[i] = (h1[i-1]*base+s[i]);
h2[i] = (h2[i-1]*base+s[n-i+1]);
p[i] = (p[i-1]*base);
}
求某一段的hash值
假设要求这一段字符的hash值,暴力的方法就是遍历这个区间,利用hash函数来求这一段字符的hash值。
其实我们可以利用已经求好的1-n的这些hash值通过计算来求这一段的hash值。
求某一段的hash值的公式:。
以上面1455583为例,现在我们求的hash值:
.
利用hash值判断回文串
如果某一段的正向hash值(如上图的h1)和反向的hash值(如上图h2)相同,那么就说这一段字符串是回文串。
code
if(h1[R]-h1[L]*p[R-L+1]) == (h2[n-R+1]-h2[n-L+1-1]*p[R-L+1]){
printf("是回文串");
}
else{
printf("不是回文串");
}
题意
定义M形字符串为两个相同的字符串拼接而成,注意第一个符串的最后一个字符可以是第二个字符串的第一个字符,也可以不是。
然后给定一段字符串,问这个字符串有多少个前缀是M形字符串。
思路
先判断前缀是不是回文串,然后分两种情况。
1)以这个前缀的最后一个字符开始取和这个前缀相同长度字符,判断这段hash值和前缀hash值是否相同。
2)以这个前缀的最后一个字符的下一个字符开始取和这个前缀相同长度字符,同样判断这段hash值和前缀hash值是否相同。
code
#include <bits/stdc++.h>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 2e6+7;
const int ds = 1e8+7;
const int base = 233;
const double PI = 3.141592653589793238462643383;
using namespace std;
char s[N];
ull h1[N],h2[N],p[N];
void solve() {
int n;
cin >> s+1;
n = strlen(s+1);
h1[0] = 0;
h2[0] = 0;
p[0] = 1;
for(int i = 1; i <= n; i++){
h1[i] = (h1[i-1]*base+s[i]);
h2[i] = (h2[i-1]*base+s[n-i+1]);
p[i] = (p[i-1]*base);
}
int ans = 0;
for(int i = 1; i <= n; i++){
if(h1[i] == (h2[n]-h2[n-i]*p[i])){//判断是不是回文串
if((h1[i]) == (h1[i+i-1]-h1[i-1]*p[i])){//情况1
ans++;
}
if((h1[i]) == (h1[i+i]-h1[i]*p[i])){//情况2
ans++;
}
}
}
cout << ans << endl;
}
int main() {
// int t;
// scanf("%d",&t);
// while(t--)
solve();
return 0;
}