万能的哈希

本章节将介绍哈希如何实现 KMP 与 manacher。

KMP

我们对于文本串 s 和模式串 t,先用一个数组 hasi 表示 s 中 前 i 位的哈希值,用 hat 表示 t 的哈希值。

然后遍历 s,计算以第 i 位为起点的长度为 |t| 的区间的哈希值,与 hat 比较,累加答案即可。

跑得飞快,复杂度碾压 KMP 好不好~

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=1e6+10;
char s[maxn],t[maxn];
int n,m,ans;
int hat,has[maxn],p[maxn]={1};
int work(int l,int r)
{
	return has[r]-has[l-1]*p[r-l+1];
}
signed main()
{
	cin>>(s+1)>>(t+1);
	n=strlen(s+1),m=strlen(t+1);
	for(int i=1;i<=n;i++)
	{
		has[i]=has[i-1]*131+(s[i]-'0');
		p[i]=p[i-1]*131;
	}
	for(int i=1;i<=m;i++)hat=hat*131+(t[i]-'0');
	for(int l=1;l<=n-m+1;l++)
	{
		int r=l+m-1;
		if(work(l,r)==hat)ans++;
	}
	cout<<ans;
	return 0;
}

manacher

设原字符串为 s,先预处理从后往前和从前往后两个哈希数组,设为 fren。那么对于一个子串 sl,r,设 get1get2 分别为两个哈希数组对于该区间的哈希值,那么它是回文串的充要条件为 get1=get2,从而可以做到 O(1) 判断回文串。

朴素做法是 O(n3) 的,用上述做法可以优化到 O(n2),依然过不去,怎么办呢?

考虑设 dpr 表示以 r 结尾的最长回文串长度。容易发现如果一个回文串以 r 结尾,如果把长度为 1 的串特判掉以后,它一定是由一个以 r1 结尾的回文串拼接两个字符组成的。

所以就找到以 r1 结尾的回文串之前的那个字符,容易推出它的位置是 rdpr1,然后我们定义 l 为从刚刚提到的位置向右移动时当前的位置,那么我们用哈希 O(1) 判断当前串 [l,r] 是否为回文串,然后如果是跳出循环,否则 ll+1 即可。

最后遍历 dp 数组,找到最大值输出即可,复杂度接近线性。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=1.1e7+10;
char s[maxn];
int n,fr[maxn],en[maxn],p[maxn];
int dp[maxn];
bool check(int l,int r)
{
	int get1=fr[r]-fr[l-1]*p[r-l+1];
	int get2=en[l]-en[r+1]*p[r-l+1];
	return get1==get2;
}
signed main()
{
	cin>>(s+1);
	n=strlen(s+1);p[0]=1;
	for(int i=1;i<=n;i++){fr[i]=fr[i-1]*171+(s[i]-'0');p[i]=p[i-1]*171;}
	for(int i=n;i>=1;i--){en[i]=en[i+1]*171+(s[i]-'0');}
	dp[1]=1;dp[2]=(s[1]==s[2]?2:1);
	for(int r=3;r<=n;r++)
	{
		int l=r-dp[r-1];
		if(l!=1)l--;
		while(l<r)
		{
			if(check(l,r))break;
			l++;
		}
		dp[r]=r-l+1;
	}
	int ans=0;
	for(int i=1;i<=n;i++)ans=max(ans,dp[i]);
	cout<<ans;
	return 0;
}
posted @   Redamancy_Lydic  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示