一.P2957 [USACO09OCT] Barn Echoes G
这道题在前介绍KMP算法时,当做例题,当然我也提到这道题可以通过字符串哈希来完成,所以现在我们来使用字符串哈希来解决,用字符串哈希的思路很明显,前后两次比较,然后取最大值就可以了,详细见代码
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define N 100005
#define base 131
ull h1[N], h2[N], p[N];
char s1[N], s2[N];
ull has(ull h[],int l,int r) //这里是计算l~r位置之间的字符串哈希值
{
return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
scanf("%s %s", s1 + 1, s2 + 1);
int len1 = strlen(s1 + 1);
int len2 = strlen(s2 + 1);
int lenm = max(len1, len2); //找出最大长度
p[0] = 1;
for (int i = 1; i <= len1; i++) //接下来两个分别计算两端字符串哈希值,前缀和形式
h1[i] = h1[i - 1] * base + s1[i];
for (int i = 1; i <= len2; i++)
h2[i] = h2[i - 1] * base + s2[i];
for (int i = 1; i <= lenm; i++) //计算p数组,用于has函数使用
p[i] = p[i - 1] * base;
int ans = -1;
for (int i = 1; i <= lenm; i++) {
int x=0, y=0;
if (i <= len1 || i <= len2) {
if (has(h1, 1, i) == has(h2, len2 - i + 1, len2)) //循环比较,各自作为前后
x = i;
if (has(h2, 1, i) == has(h1, len1 - i + 1, len1))
y = i;
ans = max(ans, max(x, y)); //取最大值
}
}
cout << ans << endl;
return 0;
}
二.P8630 [蓝桥杯 2015 国 B] 密文搜索
这一题主要的难道在于哈希值的计算,以及它的比较是每八个进行比较,要注意一下,其他和模版题差不多
#include<bits/stdc++.h>
using namespace std;
#define N 1500000
typedef unsigned long long ull;
ull f[N];
ull base[27];
string s;
ull ans, num, n;
ull hash1(string ss) //计算模版串的哈希值
{
ull k=0;
for (int i = 0; i < 8; i++) {
k+=(ull)ss[i] * base[ss[i] - 'a' + 1]; //这里的哈希值计算和平时不一样
}
return k;
}
void hash2(string s)
{
for (int i = 0; i + 8 <= s.size(); i++) { //这里每八个作为一次哈希值比较
ull k = 0;
for (int j = i; j < i + 8; j++) {
k += (ull)s[j] * base[s[j] - 'a' + 1];
}
f[i] = k; //用数组存储每八位计算的哈希值
}
}
int main()
{
for (int i = 1; i <= 26; i++) {
base[i] = i * ('a' + i); //base数组计算
}
cin >> s;
ull len = s.size();
cin >> n;
hash2(s);
while (n--) {
string ss;
cin >> ss;
ull k = hash1(ss);
for (int i = 0; i + 8 <= len; i++) { //模版串依次和母串进行比较
if (f[i] == k)
num++;
}
}
cout << num << endl;
return 0;
}
三.[ABC284F] ABCBAC
这题和第一个比较像,但比较方式不一样(字符串哈希值计算时要正着和反着各计算一次,同时由于中间翻转的字符串,我们最后要把前半部分和后半部分连接起来),同时这道题还卡unsigned long long自然溢出,所以我们必须进行取mod,具体实现过程看代码
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
const int p = 131;
#define N 2000005
typedef long long ll;
ll h1[N], h2[N], base[N];
ll n, m;
char s[N];
ll hash1(int l, int r) //获取某一区间的哈希值函数(l~r),注意其书写形式
{
return ((h1[r] - h1[l - 1] * base[r - l + 1]) % mod+mod)%mod;
}
ll hash2(int l, int r) //同上
{
return ((h2[l] - h2[r + 1] * base[r - l + 1]) % mod+mod)%mod;
}
int main()
{
cin >> n;
cin >> s + 1;
base[0] = 1;
for (int i = 1; i <= 2 * n; i++)
base[i] = base[i - 1] * p % mod; //base数组计算
for (int i = 1; i <= 2 * n; i++) //正着计算字符串哈希值
h1[i] = (h1[i - 1] * p + s[i] - 'a' + 1) % mod;
for (int i = 2 * n; i ; i--) //反正计算字符串哈希值
h2[i] = (h2[i + 1] * p + s[i] - 'a' + 1) % mod;
int flag = -1; //标记作用
for (int i = 1; i <= n + 1; i++) {
ll hs1 = hash1(1, i - 1); //前半部分正着的哈希值
ll hs2 = hash1(i + n, 2 * n); //后半部分正着的哈希值
ll ans1 = (hs1 * base[n - i + 1] + hs2) % mod; //合起来
ll ans2 = hash2(i, n + i - 1); //中间反着的哈希值
if (ans1 == ans2)
{
flag = i; //标记其位置
break;
}
}
if (flag == -1) { //不满足要求
cout << -1 << endl;
return 0;
}
for (int i = flag - 1 + n; i >= flag; i--) //输出原字符串
cout << s[i];
cout << endl;
cout << flag-1 << endl; //输出从翻转位置
return 0;
}