利用hash值判断字符串中的某一段是否为回文串

求哈希值


根据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值


假设要求[L,R]这一段字符的hash值,暴力的方法就是遍历这个区间,利用hash函数来求这一段字符的hash值。

其实我们可以利用已经求好的1-n的这些hash值通过计算来求[L,R]这一段的hash值。

求某一段的hash值的公式:h[L,R] = h[R]-h[L-1]*p^{R-L+1}

以上面1455583为例,现在我们求[3,6]的hash值:

h[6] = 145558,h[3-1] = h[2] = 14,h[2]*10^{4} = 140000

h[6]-h[2]*10^{4} = 5558.

利用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;
}

 

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值