Hash专题练习记录 1

HDU 4080 Stammering Aliens

Solution:

\(Hash\)。二分长度+哈希,比较明显的做法,注意细节即可。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll jc[N];
ll mod=386910137;
ll bas=13331;
ll ha[N];
char s[N];
ll getlr(int l,int r){
	return ((ha[r]-ha[l-1]*jc[r-l+1])%mod+mod)%mod;
}
void init(){
	jc[0]=1;
	for(int i=1;i<=N-1;++i){
		jc[i]=(jc[i-1]*bas)%mod;
	}
}
int n;
int pos;
#define debug(x) cout<<#x<<" :"<<x<<endl
unordered_map<int,int>mp;
bool check(int mid,int lens){
	mp.clear();
	int kep=0;
	for(int i=1;i<=lens-mid+1;i++){
		int l=i;
		int r=l+mid-1;
		mp[getlr(l,r)]++;
		if(mp[getlr(l,r)]>=n){
			return true;
		}
	}

	return false;
}

int getpos(int lens,int len){
	mp.clear();
	int kep=0;
	for(int i=1;i<=lens-len+1;i++){
		int l=i;
		int r=l+len-1;

		mp[getlr(l,r)]++;
		if(mp[getlr(l,r)]>=n){
			kep=l;
		}
	}
	return kep;
}
int main(){
	init();
	while(scanf("%d",&n)&&n){
		scanf("%s",s+1);
		int lens=strlen(s+1);
		for(int i=1;i<=lens;++i){
			ha[i]=(ha[i-1]*bas+s[i])%mod;
		}
		int L=1;
		int R=lens;
		int ans=0;
		int mid;
		int pos=0;
		while(L<=R){
			mid=(L+R)>>1;
			if(check(mid,lens)){
				ans=mid;
				L=mid+1;
			}
			else R=mid-1;
		}
		if(ans==0)puts("none");
		else {
			printf("%d %d\n",ans,getpos(lens,ans)-1);
		}
	}
	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

Solution:

\(Hash\)。此题有奇奇怪怪的姿势可过,这里使用了手写\(hashmap\)来降低常数,其余地方按照题意统计即可。

Code:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<iostream>
#include<map>
#include<set>
#define debug(x) cout<<#x<<" :"<<x<<endl
using namespace std;
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define fi first
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e6+10;

ll jc[N];
ll mod=386910137;
ll bas=13331;
ll ha[N];
char s[N];
ll getlr(int l,int r){
	return ((ha[r]-ha[l-1]*jc[r-l+1])%mod+mod)%mod;
}
void init(){
	jc[0]=1;
	for(int i=1;i<=N-1;++i){
		jc[i]=(jc[i-1]*bas)%mod;
	}
}
int n,m;
int pos;
const int maxsz=4e6+9;//@素数表@:1e7+19,2e7+3,3e7+23
//1e6+3,2e6+3,3e6+7,4e6+9,1e5+3,2e5+3,3e5+7,4e5+9
//@要保证取值的操作次数小于maxsz,maxsz最好为素数@
//@count操作不增加新节点@
class hash_map{public:
  struct node{ll u;int v,next;}e[maxsz<<1];
  int head[maxsz],nume,numk,id[maxsz];
  bool count(ll u){
    int hs=u%maxsz;
    for(int i=head[hs];i;i=e[i].next)
      if(e[i].u==u) return 1;
    return 0;
  }
  int& operator[](ll u){
    int hs=u%maxsz;
    for(int i=head[hs];i;i=e[i].next)
      if(e[i].u==u) return e[i].v;
    if(!head[hs])id[++numk]=hs;
    return e[++nume]=(node){u,0,head[hs]},head[hs]=nume,e[nume].v;
  }
  void clear(){
    rep(i,0,numk)head[id[i]]=0;
    numk=nume=0;
  }
};

hash_map mp;//定义
int main(){
	init();
	while(scanf("%d%d",&n,&m)!=EOF){

		int ans=0;
		scanf("%s",s+1);
		int lens=strlen(s+1);
		for(int i=1;i<=lens;++i){
			ha[i]=(ha[i-1]*bas+s[i])%mod;
		}
		for(int i=1;i<=lens-n+1;++i){
			int l=i;
			int r=l+n-1;
			if(mp[getlr(l,r)]==0){
				mp[getlr(l,r)]++;
				ans++;
			}
		}
		printf("%d\n",ans);
		mp.clear();

	}
	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

HDU 4886 TIANKENG’s restaurant(Ⅱ)

Solution:

\(Hash\)。细节蛮多的一道题。
1、观察题意数据量,字符串长度不超过\(1000000\)。假设答案字符串长度为\(anslen=8\),那么\(8^8>1000000\),所以\(anslen\)不可能为\(8\)。逐渐推导得到\(anslen<=7\)

2、依次枚举\(anslen=[1,7]\)的所有子串,先求出在主串中相同长度子串的\(hash\)值并标记,随后遍历从\([0,MAX]\)\(hash\)值,遇到第一个未被标记的\(hash\)值即为答案的\(hash\)值。

\(MAX\)值为当前长度的\(hash\)值的最大上限,例如十进制长度为\(3\)的值的最大上限为\(9*10^2+9*10^1+9*10^0\)

所有\(hash\)值都是基于\(8\)进制下的\(hash\)值,\(9\)进制也可以通过此题。

3、求主串中某个长度的所有子串时,不能暴力求,会被卡常。考虑滑动窗口的思想。例如十进制下当前\(s=1234\),先求出第一个长度为\(3\)\(hash1=123\),如何快速转移到下一步,令\(hash2=(hash1)mod100*10+4\)即可,会剩下巨大的计算量。

4、目前还没发现其他做法可通过此题,除了上述两种,有兴趣的同学可大胆尝试其他做法。

Code:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<iostream>
#include<map>
#include<set>
#define debug(x) cout<<#x<<" :"<<x<<endl
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
typedef long long ll;
const int N=6e6+10;
ll mod=386910137;
int bas=8;
char s[N];
char kep[N];
int flag=0;
char ans[20];

bool mp[3000005];//定义
int jc[20];
int n;
int pos;
int main(){
	int t;
	jc[0]=1;
	for(int i=1;i<=7;++i){
		jc[i]=jc[i-1]*8;
	}
	scanf("%d",&t);
	while(t--){
		scanf("%s",s+1);
		int lens=strlen(s+1);
		int mx=0;
		flag=0;
		int Mx=1;
		for(int L=1;L<=7;++L){
			Mx*=8;
			for(int i=0;i<Mx;++i)mp[i]=0;
			int has=0;
			for(int i=1;i<=L;++i){
				has=(has*bas+s[i]-'A');
			}
			mp[has]=1;
			for(int i=L+1;i<=lens;++i){
				has%=jc[L-1];
				has=has*bas+s[i]-'A';
				mp[has]=1;
			}
			for(int i=0;i<Mx;++i){
				if(mp[i]==0){
					int val = i;
					int cnt = 0;
					for(int j=L-1;j>=0;--j){
						ans[++cnt] = val/jc[j] + 'A';
						val%=jc[j];
					}
					ans[cnt+1]='\0';
					printf("%s\n",ans+1);
					flag=1;
					break;
				}
			}
			if(flag)break;
		}
	}
	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

HDU 1800 Flying to the Mars

Solution:

\(Hash\)。算是考察姿势的一道题吧,姿势不对无法通过。

观察到可能会有前缀为\(0\)的字符串,根据题意由于是比较数字,那么\(001=1\),所以如果\(hash\)姿势为\(ha=ha*bas+s[i]\),很顺利成章的无法通过。

注意细节,其余正常操作即可。

Code:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int N=2e5+10;

ll jc[N];
ll mod=386910137;
ll bas=13331;
ll ha[N];
char s[N];
ll getlr(int l,int r){
	return ((ha[r]-ha[l-1]*jc[r-l+1])%mod+mod)%mod;
}
void init(){
	jc[0]=1;
	for(int i=1;i<=N-1;++i){
		jc[i]=(jc[i-1]*bas)%mod;
	}
}
int n,m;
int pos;
map<ll,int>mp;
int main(){
	init();
	while(scanf("%d",&n)!=EOF){
		int mx=0;
		for(int h=1;h<=n;++h){
			scanf("%s",s+1);
			int lens=strlen(s+1);
			for(int i=1;i<=lens;++i){
				ha[i]=(ha[i-1]*bas+s[i]-'0')%mod;
			}
			mp[ha[lens]]++;
			mx=max(mp[ha[lens]],mx);
		}
		printf("%d\n",mx);
		mp.clear();
	}

	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

HDU 1496 Equations

Solution:

\(Hash\)经典题。观察发现如果直接暴力复杂度无法通过。

考虑将式子变为\(a*x1^2+b*x2^2=-c*x3^2-d*x4^2\),那么枚举左侧的双重循环,求出\(hash_{left}\)值并标记出现次数\(cnt[hash_{left}]++\),再对枚举右侧的双重循环,求出\(hash_{right}\)值,每一个\(hash_{right}\)值对答案的贡献就显然为\(cnt[hash_{right}]\),最后累加即可。

考虑卡常,可以将\(hash\)值默认为计算结果,对于负数,加上一个上限值即可,内存开得下。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
int a,b,c,d;
int mp[N];
int main(){
	while(scanf("%d%d%d%d",&a,&b,&c,&d)!=EOF){
		
		for(int i=-100;i<=100;++i){
			for(int j=-100;j<=100;++j){
				if(i==0||j==0)continue;
				mp[a*i*i+b*j*j+1000000]++;
			}
		}
		int cnt=0;
		for(int i=-100;i<=100;++i){
			for(int j=-100;j<=100;++j){
				if(i==0||j==0)continue;
				cnt+=mp[1000000-(c*i*i+d*j*j)];
			}
		}
		for(int i=-100;i<=100;++i){
			for(int j=-100;j<=100;++j){
				if(i==0||j==0)continue;
				mp[a*i*i+b*j*j+1000000]=0;
			}
		}
		printf("%d\n",cnt);
	}
	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

HDU 1711 Number Sequence

Solution:

\(Hash\)。比较裸的\(Hash\)统计题,直接利用\(Hash\)做题即可,注意负数的情况,所有数加上上限值即可。

Code:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int N=1e6+10;

ll jc[N];
ll mod=386910137;
ll bas=13331;
ll ha[N];
int a[N];
int b[N];
char s[N];
ll getlr(int l,int r){
	return ((ha[r]-ha[l-1]*jc[r-l+1])%mod+mod)%mod;
}
void init(){
	jc[0]=1;
	for(int i=1;i<=N-1;++i){
		jc[i]=(jc[i-1]*bas)%mod;
	}
}
int n,m;
int pos;
set<ll>se;
int main(){
	init();
	int t;
	cin>>t;
	while(t--){
		scanf("%d%d",&n,&m);
		ll ha1=0;
		for(int i=1;i<=n;++i)scanf("%d",&a[i]);
		for(int i=1;i<=m;++i)scanf("%d",&b[i]);
		for(int i=1;i<=n;++i){
		 	a[i]+=1000000;
		}
		for(int i=1;i<=m;++i)b[i]+=1000000;
		for(int i=1;i<=m;++i){
			ha1=(ha1*bas+b[i])%mod;
		}
		for(int i=1;i<=n;++i){
			ha[i]=(ha[i-1]*bas+a[i])%mod;
		}
		int cnt = -1;
		for(int i=1;i<=n-m+1;++i){
			if(getlr(i,i+m-1)==ha1){
				cnt = i;break;
			}
		}
		printf("%d\n",cnt);
	}

	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

HDU 1686 Oulipo

Solution:

\(Hash\),经典裸题,直接做即可。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll jc[N];
ll mod=386910137;
ll bas=13331;
ll ha[N];
char s[N];
char p[N];
ll getlr(int l,int r){
	return ((ha[r]-ha[l-1]*jc[r-l+1])%mod+mod)%mod;
}
void init(){
	jc[0]=1;
	for(int i=1;i<=N-1;++i){
		jc[i]=(jc[i-1]*bas)%mod;
	}
}
int n;
int pos;
int main(){
	init();
	int n;
	scanf("%d",&n);
	while(n--){
		scanf("%s",s+1);
		int lens=strlen(s+1);
		ll hah=0;
		for(int i=1;i<=lens;++i){
			hah=(hah*bas+s[i])%mod;
		}
		scanf("%s",p+1);
		int lenp=strlen(p+1);
		for(int i=1;i<=lenp;++i){
			ha[i]=(ha[i-1]*bas+p[i])%mod;
		}
		int cnt=0;
		for(int i=1;i<=lenp-lens+1;++i){
			int l = i;
			int r = i+lens-1;
			if(getlr(l,r)==hah)cnt++;
		}
		printf("%d\n",cnt);
	}
	return 0;
}

\(\rule[0pt]{38.3cm}{0.05cm}\)

HDU 2087 剪花布条

Solution:

\(Hash\),经典裸题,注意到匹配的要求是不可重叠。当匹配到一个符合要求的串时,直接跳过这个串的长度即可。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
ll jc[N];
ll mod=386910137;
ll bas=13331;
ll ha[N];
char s[N];
char p[N];
ll getlr(int l,int r){
	return ((ha[r]-ha[l-1]*jc[r-l+1])%mod+mod)%mod;
}
void init(){
	jc[0]=1;
	for(int i=1;i<=N-1;++i){
		jc[i]=(jc[i-1]*bas)%mod;
	}
}
int n;
int pos;
int main(){
	init();
	int n;
	scanf("%d",&n);
	while(scanf("%s",s+1)!=EOF){
		if(s[1]=='#')break;
		int lens=strlen(s+1);
		ll hah=0;
		for(int i=1;i<=lens;++i){
			ha[i]=(ha[i-1]*bas+s[i])%mod;
		}
		scanf("%s",p+1);
		int lenp=strlen(p+1);
		for(int i=1;i<=lenp;++i){
			hah=(hah*bas+p[i])%mod;
		}
		int cnt=0;
		for(int i=1;i<=lens-lenp+1;++i){
			int l = i;
			int r = i+lenp-1;
			if(getlr(l,r)==hah){
				cnt++;
				i=r;
			}
		}
		printf("%d\n",cnt);
	}
	return 0;
}
posted @ 2021-07-16 23:13  Qquun  阅读(63)  评论(0)    收藏  举报