【Ybt OJ】[字符串算法 第2章] 哈希Hash [前半章]

「 「 字符串算法 」 」 2 2 2 H a s h Hash Hash H a s h Hash Hash表(前 3 3 3题)
后半章 l i n k link link
目录:

A.字符串哈希
B.回文子串
C.对称正方形

A . A. A. 例题 1 1 1 字符串哈希

洛谷 l i n k link link
在这里插入图片描述

分析:

可以直接暴力找 跑的比单哈希还快 可还行.
也可以一个哈希搞完 也可以无错哈希
以及字典树. ( b u s h i (bushi bushi

暴力CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
//#pragma GCC optimize(2)
#define reg register 
using namespace std;
int n,tot;
string x[10005];
int main()
{
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		cin>>x[i];
	sort(x+1,x+n+1); //字典序排完
	for(int i=1;i<=n;i++)
		if(x[i]==x[i+1]) tot++;
	printf("%d",n-tot);  //一共-相同=不同
	return 0;
}

单哈希CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
//#pragma GCC optimize(2)
#define reg register 
using namespace std;
typedef long long ll;
typedef double db;
const int N=10005;
const int e=131;
const long long p=200709081011;
int n,a[N],ans=1;
string s;
int locate(string x)
{
    
    
	int len=x.length();
	int tot=0;
	for(int i=0;i<len;i++)
		tot=(tot*e+(int)x[i])%p;  //字符串哈希
	return tot;
}
int main(){
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
    
    
		cin>>s;
		a[i]=locate(s);
	}
	sort(a+1,a+n+1);
	for(int i=1;i<n;i++)
		if(a[i]!=a[i+1]) ans++;
	printf("%d",ans);
	return 0; 
}

B . B. B. 例题 2 2 2 回文子串

在这里插入图片描述

分析:

众所周知 马拉车 ( M a n a c h e r ) (Manacher) (Manacher)算法是处理这类问题的好算法
所以我们要用二分 + + +哈希来做 ( ? (? (? ? ? ? ? ) ?) ?)
先枚举回文串的中心 ( ( (对称点 ) ) ) 然后就二分长度即可
怎么找中心就是 两个相对位置两两匹配上 最后的位置
但是不能一个个枚举 肯定会 T T T 所以要扔进 H a s h Hash Hash里提效率.

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
#pragma GCC optimize(2)
#define reg register 
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1000005;
const unsigned long long e=131;
const long long p=200709081011;
int n,l,r,mid,ans,Case;
ull Pos[N],Rev[N],Base[N];
char c[N];
bool End()
{
    
    
	if(c[1]=='E'&&c[2]=='N'&&c[3]=='D') return 1;
	return 0;
}
void Pre()
{
    
    
	memset(Pos,0,sizeof(Pos));
	memset(Rev,0,sizeof(Rev));
	memset(Base,0,sizeof(Base));
	Base[0]=1ull;
	for(reg int i=1;i<=n;i++)
	{
    
    
		Base[i]=(Base[i-1]*e);
		Pos[i]=(Pos[i-1]*e+(c[i]-'a'));  //正序字符串哈希
	}
	for(reg int i=n;i>0;i--)
		Rev[i]=(Rev[i+1]*e+(c[i]-'a'));  //倒序字符串哈希
}
void work()
{
    
    
	for(reg int i=1;i<=n;i++)  //找中心
	{
    
    
		l=0;r=n;
		while(l<=r)  //长度奇数
		{
    
    
			mid=(l+r)>>1;
			if(i-mid<1||i+mid>n){
    
    r=mid-1; continue;}
			if(Pos[i]-Pos[i-mid-1]*Base[mid+1]==Rev[i]-Rev[i+mid+1]*Base[mid+1])
			{
    
    
				ans=max(ans,mid*2+1); 
				l=mid+1;
			}
			else r=mid-1;
		}
		l=0;r=n;
		while(l<=r)  //长度偶数
		{
    
    
			mid=(l+r)>>1;
			if(i-mid+1<1||i+mid>n){
    
    r=mid-1; continue;}
			if(Pos[i]-Pos[i-mid]*Base[mid]==Rev[i+1]-Rev[i+mid+1]*Base[mid])
			{
    
    
				ans=max(ans,mid*2);
				l=mid+1;
			}
			else r=mid-1;
		}
	}
}
int main(){
    
    
	scanf("%s",c+1);
	n=strlen(c+1);
	while(!End())
	{
    
    
		Case++;
		ans=1;
		Pre();
		work();
		printf("Case %d: %d\n",Case,ans);
		memset(c,0,sizeof(c));
		scanf("%s",c+1);
		n=strlen(c+1);
	}
	return 0; 
}

C . C. C. 例题 3 3 3 对称正方形

洛谷 l i n k link link
在这里插入图片描述

分析:

例题 3 3 3直接省选 可还行
不难发现 这其实是个二维马拉车 ( M a n a c h e r ) (Manacher) (Manacher)
所以我们要用二维哈希+二分答案 ( (
马拉车算法实现复杂 效率快 如果范围允许 二维哈希+二分是更优的
这道题实际上与上题类似 不过是二维的……
我们肯定要先找正方形的对称轴的 但这样不好处理
所以我们可以先找正方形中心点横向纵向比较 像一维统计答案就可以了
以及要用二维 H a s h Hash Hash T a b l e Table Table来提效率 … … ……
具体怎么处理 就跟二维前缀和类似 行列分别做就行了 翻转正方形也是一样处理
判断完回文后 就二分答案即可 最后加上 n ∗ m n*m nm 因为每个 1 ∗ 1 1*1 11格子也算一个正方形

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
//#pragma GCC optimize(2)
#define reg register 
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1005;
ull G[N][N],Xturn_G[N][N],Yturn_G[N][N];
ull Base_x[N],Base_y[N];
const ull p1=131,p2=313;
int n,m,ans;
void PreTurn()
{
    
    
	for(reg int i=1;i<=n;i++)
		for(reg int j=1;j<=m;j++)
		{
    
    
			scanf("%lld",&G[i][j]);
			Xturn_G[n-i+1][j]=Yturn_G[i][m-j+1]=G[i][j];  //翻转
		} 
}
void Hash_Work()
{
    
    
	Base_x[0]=Base_y[0]=1;
	for(reg int i=1;i<=max(n,m);i++)
	{
    
    
		Base_x[i]=Base_x[i-1]*p1;  //二维哈希
		Base_y[i]=Base_y[i-1]*p2;
	}
	for(reg int i=1;i<=n;i++)
		for(reg int j=1;j<=m;j++)
		{
    
    
			G[i][j]+=G[i][j-1]*p1;
			Xturn_G[i][j]+=Xturn_G[i][j-1]*p1;
			Yturn_G[i][j]+=Yturn_G[i][j-1]*p1;
		}
	for(reg int i=1;i<=n;i++)
		for(reg int j=1;j<=m;j++)
		{
    
    
			G[i][j]+=G[i-1][j]*p2;
			Xturn_G[i][j]+=Xturn_G[i-1][j]*p2;  //处理行列的哈希
			Yturn_G[i][j]+=Yturn_G[i-1][j]*p2;
		}
}
ull Value(int kd,int x,int y,int len)  //(x,y)为中心点坐标 len为枚举的方形边长
{
    
    
	if(kd==1) return G[x][y]-G[x-len][y]*Base_y[len]-G[x][y-len]*Base_x[len]+G[x-len][y-len]*Base_x[len]*Base_y[len];  //原地
	if(kd==2){
    
      //纵向翻转
		y=m-(y-len);
		return Yturn_G[x][y]-Yturn_G[x-len][y]*Base_y[len]-Yturn_G[x][y-len]*Base_x[len]+Yturn_G[x-len][y-len]*Base_x[len]*Base_y[len];
	}
	if(kd==3){
    
      //横向翻转
		x=n-(x-len);
		return Xturn_G[x][y]-Xturn_G[x-len][y]*Base_y[len]-Xturn_G[x][y-len]*Base_x[len]+Xturn_G[x-len][y-len]*Base_x[len]*Base_y[len];
	}
}
bool check(int x,int y,int len)  //意义同上
{
    
    
	if(x<len||x>n||y<len||y>m) return 0;
	ull val1,val2,val3;
	val1=Value(1,x,y,len);
	val2=Value(2,x,y,len);
	val3=Value(3,x,y,len);
	return (val1==val2)&&(val2==val3);  //判断对称
}
void Even_Work()  //大小为偶数
{
    
    
	for(reg int i=1;i<n;i++)
		for(reg int j=1;j<m;j++)
		{
    
    
			int l=0,r=max(m,n)+1,temp=0;
			while(l<=r)
			{
    
    
				int mid=(l+r)>>1;
				if(check(i+mid,j+mid,mid*2)){
    
      //二分答案
					temp=mid;
					l=mid+1;
				}else 
					r=mid-1;
			}
			ans+=temp;
		}
}
void Odd_Work()  //大小为奇数
{
    
    
	for(reg int i=1;i<=n;i++)
		for(reg int j=1;j<=m;j++)
		{
    
    
			int l=0,r=max(n,m)+1,temp=0;
			while(l<=r)
			{
    
    
				int mid=(l+r)>>1;
				if(check(i+mid,j+mid,mid*2+1)){
    
    
					temp=mid;
					l=mid+1;
				}else
					r=mid-1;
			}
			ans+=temp;
		}
}
int main(){
    
    
	scanf("%d%d",&n,&m);
	PreTurn();
	Hash_Work();
	Even_Work();
	Odd_Work();
	ans+=n*m;  //得出最终ans
	printf("%d",ans);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/112997239
今日推荐