字符串哈希相关例题题解

一.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;
}

 

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值