【总结】字符串hash(7.25)

一.概述

Hash算法可以将一个数据转换为一个标志,这个标志和源数据的每一个字节都有十分紧密的关系。Hash算法还具有一个特点,就是很难找到逆向规律。
Hash算法是一个广义的算法,也可以认为是一种思想,使用Hash算法可以提高存储空间的利用率,可以提高数据的查询效率,也可以做数字签名来保障数据传递的安全性。所以Hash算法被广泛地应用在互联网应用中。 [1]
Hash算法也被称为散列算法,Hash算法虽然被称为算法,但实际上它更像是一种思想。Hash算法没有一个固定的公式,只要符合散列思想的算法都可以被称为是Hash算法。 [2]

实现方式:取一个固定值P,把字符串看作P进制数,并分配一个大于0的数值,代表每种字符。取一固定值M,求出该P进制数对M的余数,作为该字符串的Hash值。可以使用unsigned long long类型存储这个Hash值。该算法很难产生冲突。一般取P=131或P=13331

二.例题

A.兔子与兔子

解析:求hash前缀和p,可以O(1)求出区间的hash值

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
char s[maxn];
unsigned long long f[maxn],p[maxn];
int q,n;
int main() {
    
    
	scanf("%s",s+1);
	n=strlen(s+1);
	f[0]=1;
	for(int i=1;i<=n;i++) {
    
    
		p[i]=p[i-1]*131+s[i]-'a';
		f[i]=f[i-1]*131;
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++) {
    
    
		int l1,r1,l2,r2;
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		if(p[r1]-p[l1-1]*f[r1-l1+1]==p[r2]-p[l2-1]*f[r2-l2+1]) printf("Yes\n");
		else printf("No\n"); 
	}
}

B.雪花雪花雪花

解析:令 H ( a 1 , a 2 , . . . a 6 ) = a 1 ∗ a 2 ∗ . . . ∗ a 6 + a 1 + a 2 + . . . + a 6 H(a1,a2,...a6)=a1*a2*...*a6+a1+a2+...+a6 Ha1,a2,...a6=a1a2...a6+a1+a2+...+a6
本题hash值是个轮换函数,其实并不是完美的字符串hash,所以要用链表解决冲突。hash只能判断两个完全相同的字符串,而本题大可不必将所有排列都枚举出来。

#include<bits/stdc++.h>
using namespace std;
const int maxn=100010,P=99991;
struct node{
    
    
	int a[6];
}snow[maxn];
int n,head[maxn],next[maxn],cnt;
bool equal(int *a,int *b) {
    
    
	for(int i=0;i<6;i++) {
    
    
		bool ok1=1,ok2=1;
		for(int j=0;j<6;j++) {
    
    
			if(a[j]!=b[(i+j)%6]) ok1=0;
			if(a[j]!=b[(i-j+5)%6]) ok2=0;
		}
		if(ok1||ok2) return 1;
	}
	return 0;
}
int hash(int *a) {
    
    
	long long sum=0,mul=1;
	for(int i=0;i<6;i++) {
    
    
		sum=(sum+a[i])%P;
		mul=(mul*a[i])%P;
	}
	return (sum+mul)%P;
}
bool insert(int *a) {
    
    
	int val=hash(a);
	for(int t=head[val];t;t=next[t]) {
    
    
		if(equal(snow[t].a,a)) return 1;
	}
	next[cnt]=head[val];
	head[val]=cnt;
	return 0;
}
int main() {
    
    
	scanf("%d",&n);
	for(cnt=1;cnt<=n;cnt++) {
    
    
		for(int j=0;j<6;j++) {
    
    
			scanf("%d",&snow[cnt].a[j]);
		}
		if(insert(snow[cnt].a)) {
    
    
			printf("Twin snowflakes found.\n");
			return 0;
		}
	}
	printf("No two snowflakes are alike.");
}

C.Hash 键值 (hash)

思路:本题并不是hash,而是将 O ( n ∗ n ) O(n*n) O(nn)优化成了 O ( n ∗ s q r t ( n ) ) O(n*sqrt(n)) O(nsqrt(n))

具体做法是:

  1. 对于x<=sqrt(n),用 O ( s q r t ( n ) ) O(sqrt(n)) O(sqrt(n))维护数组, O ( 1 ) O(1) O(1)查讯
  2. 对于x>sqrt(n),用 O ( 1 ) O(1) O(1)维护数组, O ( s q r t ( n ) ) O(sqrt(n)) O(sqrt(n))查讯

相当于用两种做法分摊了时间复杂度,本题的解法不禁让人想到了巧妙的前缀和优化。说明有的最优的解法其实取决于数据,并不一定只与m,n有关。

#include<bits/stdc++.h>
using namespace std;
int n,m,q,a[100005],hash[100005][405];
int main() {
    
    
	freopen("hash.in","r",stdin);
	freopen("hash.out","w",stdout);
	scanf("%d%d",&n,&q);
	m=sqrt(n);
	for(int i=1;i<=n;i++) {
    
    
		scanf("%d",&a[i]);
		for(int j=1;j<=m;j++) {
    
    
			hash[j][i%j]+=a[i];
		}
	}
	for(int i=1;i<=q;i++) {
    
    
		int ok,x,y,tot=0;
		scanf("%d%d%d",&ok,&x,&y);
		if(ok==1) {
    
    
			if(x<=m) printf("%d\n",hash[x][y]);
			else {
    
    
				for(int j=y;j<=n;j+=x) tot+=a[j];
				printf("%d\n",tot);
			}
		}
		else {
    
    
			for(int j=1;j<=m;j++) {
    
    
				hash[j][x%j]+=y-a[x];
			}
			a[x]=y;
		}
	}
} 

D.三个朋友

思路:这道题其实并不难。枚举去掉的点,剩下的2*len的序列,只需判断 h a s h ( 1 , l e n ) = = h a s h ( l e n + 1 , l e n ∗ 2 ) hash(1,len)==hash(len+1,len*2) hash(1,len)==hash(len+1,len2)
而原始字符串只可能是(1,len)或(len+2,n),当且仅当两个字符串不相等且均为解时输出"NOT UNIQUE".

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
int n,len;
char a[2000005];
unsigned long long p[2000005],f[2000005];
bool f1,f2;
unsigned long long hash(int le,int ri) {
    
    
	if(le>ri) return 0;
	return p[ri]-p[le-1]*f[ri-le+1];
}
int main() {
    
    
	f[0]=1;
	scanf("%d",&n);
	scanf("%s",a+1);
	if(n%2==0) {
    
    
		printf("NOT POSSIBLE");
		return 0;
	}
	len=n/2;
	for(int i=1;i<=n;i++) {
    
    
		f[i]=f[i-1]*31;
		p[i]=p[i-1]*31+a[i]-'A';
	}
	for(int i=1;i<=len+1;i++) {
    
    
		if(hash(1,i-1)*f[len-i+1]+hash(i+1,len+1)==hash(len+2,n)) {
    
    
			f1=1;
			break;
		}
	}
	for(int i=len+2;i<=n;i++) {
    
    
		if(hash(1,len)==hash(len+1,i-1)*f[n-i]+hash(i+1,n)) {
    
    
			f2=1;
			break;
		}
	}
	if(!f1&&!f2) printf("NOT POSSIBLE");
	else if(f1&&f2&&hash(1,len)!=hash(len+2,n)) printf("NOT UNIQUE");
	else {
    
    
		if(f2) for(int i=1;i<=len;i++) printf("%c",a[i]);
		else for(int i=len+2;i<=n;i++) printf("%c",a[i]);
	}
}

猜你喜欢

转载自blog.csdn.net/cqbzlydd/article/details/107549202