7-12 最长对称子串(分数 25) 新解法——字符串哈希精讲!!!

文章介绍了如何利用字符串哈希(前缀哈希法)计算字符串的哈希值,通过比较字符串与其翻转后的部分的哈希值,找出最长对称子串的长度。通过取模确保哈希值的独特性,解决给定字符串的对称性问题。
摘要由CSDN通过智能技术生成

题目要求:

对给定的字符串,本题要求你输出最长对称子串的长度。例如,给定Is PAT&TAP symmetric?,最长对称子串为s PAT&TAP s,于是你应该输出11。

输入格式:

输入在一行中给出长度不超过1000的非空字符串。

输出格式:

在一行中输出最长对称子串的长度。

输入样例:

Is PAT&TAP symmetric?

输出样例:

11

思路: 

         字符串哈希:

字符串哈希全称字符串前缀哈希法,把字符串变成一个p进制数字(哈希值),实现不同的字符串映射到不同的数字。
对形如 str = X_1X_2X_3...{X_{n-}}_1X_n 的字符串,采用字符的ASCII码乘上 P 的次方来计算哈希值。

映射公式value = X_1*P^{n-1}+X_2*P^{n-2}+...+X_{n-1}*P^1+X_n*P^0

即字符串str映射成值为value。

设s为下标从1开始的字符串,h[i]代表字符串s前i个字符的构成的字串的哈希值, p[i]代表k的i次方。

根据定义与公式分别求出h[i]:
则h[i] = s[1] * p[i - 1] + s[2] * p[i - 2] + ... + s[i - 1] * p[1] + s[i] * p[0]。

因为规定s下标从0开始,p[i]代表P的i次方。所以p[0] = 1。由此得:
h[0] = 0;
h[1] = s[1] * p[0] = h[1]
h[2] = s[1] * p[1] + s[2] * p[0] = h[1] * k + s[2]
h[3] = s[1] * p[2] + s[2] * p[1] + s[3] * p[0] = h[2] * k + s[3]
.
.
.
h[i - 1] = s[1] * p[i - 2] + s[2] * p[i - 3] +...+ s[i - 2] * p[1] + s[i - 1] * p[0] = h[i - 2] * k + s[i - 1];
h[i] = s[1] * p[i - 1] + s[2] * p[i - 2] + ... + s[i - 1] * p[1] + s[i] * p[0] = h[i - 1] * k + s[i]


由以上公式得出h[i] = h[i - 1] * k + s[i];
而单纯这样算很容易超出数据范围,因此对哈希值取余
有的小伙伴可能会觉得这样容易导致不同字符串有相同得hash值。
不用担心,经过实验与论证,当k取131或13331,取余mod取2的64次方时,可唯一确定一个字符串对应一个哈希值。

当我们要求字串的hash值时,由于规定h[i] 为前i个字符组成的字串的哈希值,因此可利用前缀和公式,但在这里要进行小小的变形

假设求一个字符串[l, r]之间字串的哈希值,value= h[r] - h[l] * p[r - l + 1]。

例如:求X_1X_2X_3X_4X_5X_6X_7这个字符串中X_2X_3X_4这个字串的哈希值,即求下标[2, 3]字串,value = h[3] - h[2] * p[3 - 2 + 1] 。

这是由于我们要讲大串与小串最后一位对齐。X_1X_2X_3X_4 \\ .\quad X_2X_3X_4,因此h[l]要乘p的r-l+1次方。

        本题:

    输入字符串str1后,将str1赋给str2,str2前后翻转,使str1和str2翻转。这样做的目的是,为了求对称字串,判断一个字符串str1的[l, r]是对称串,我们只需判断str1的[l, r]与str2[n - r + 1, n - l + 1]的哈希值是否一致。相同则是对称字串。

    记得将str1和str2设置成下标1开始的子串!!!

本题代码:

    单个字符算对称串,最后答案不能为0,最小为1!!!

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;

const int N = 10010, P = 131;
int n, res;    //res为最终答案
ULL h1[N], h2[N], p[N];    //h1[]为str1对应哈希值,h2[]为str2对应哈希值,p[i]为P的i次方
string str1, str2;

ULL get(int l, int r, ULL h[])
{
	return h[r] - h[l - 1] * p[r - l + 1];
}

int main(){
	getline(cin, str1);    //输入
	n = str1.size();
	str1 += '.';
	str2 = str1, reverse(str2.begin(), str2.end());    //翻转后字符串str2下标已从1开始
	for(int i = str1.size() - 2; i >= 0; i --) str1[i + 1] = str1[i]; //将str1下标设为从1开始 
	
	p[0] = 1;
	for(int i = 1; i <= n; i ++) //哈希赋值
	{
		p[i] = p[i - 1] * P;
		h1[i] = h1[i - 1] * P + str1[i];
		h2[i] = h2[i - 1] * P + str2[i];
	}
	for(int i = 1; i <= n; i ++)
		for(int j = i; j <= n; j ++)
			if(get(i, j, h1) == get(n - j + 1, n - i + 1, h2)) //比较
				res = max(res, j - i + 1);    //取最大值
	cout << res;
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值