哈希
目录
多哈希板子
struct Hash{
//胡老板的板子很干净,但多哈希相对慢一些,大约两倍时长
//从下标为1开始用
int base[4],mod[4];
int tot,hash[4][maxn],pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i
Hash() {
tot=0;
for(int i=1;i<=3;i++) pw[i][0]=1;
base[1]=233;base[2]=19260817;base[3]=20030714;//其余模数 39
mod[1]=1e9+7;mod[2]=1e9+9;mod[3]=998244353;
}
void init() {tot=0;}
void insert(int c) {
tot++;
for(int i=1;i<=3;i++) hash[i][tot]=(1LL*hash[i][tot-1]*base[i]+c)%mod[i];
for(int i=1;i<=3;i++) pw[i][tot]=(1LL*pw[i][tot-1]*base[i])%mod[i];
}
//字符串[l,r]hash值,type为第几个hash
int query(int l,int r,int type) {
return (hash[type][r]-(1LL*hash[type][l-1]*pw[type][r-l+1]%mod[type])+mod[type])%mod[type];
}
//判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同
friend bool same(Hash &u,int lu,int ru,Hash &v,int lv,int rv) {
if(ru-lu!=rv-lv) return false;
for(int i=1;i<=3;i++) if(u.query(lu,ru,i)!=v.query(lv,rv,i)) return false;
return true;
}
}h1,h2;
相似字符串:E - Check Transcription
题意:给你一个01串s(\(2≤|s|≤10^5\)) ,给你一个普通字符串t(\(1≤|t|≤10^6\)),请你找有多少种方案令"0"=s0,"1"=s1,使得s映射后=t,要求s0不等于s1,且两者非空。题目保证s串中至少有一个0和1。
题解:没有题解,直接哈希
注意点:
1.复杂度计算,次数大约是\(\sum_{i=1}^t\frac{t}{i}\approx tln(t)\),所以才可以这样哈希过,推导过程:
注:倒数第二行的角标写错了i--1
当你在cf这种地方想要单哈希过题,而且想用自然溢出;
当你几个小时终于妥协决定加个模数
3.strlen(s+1)//读入的时候有+1,这里不能忘记+1,不然debug会像我一样痛苦
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<':'<<x<<endl;
typedef unsigned long long ull;
const int maxn=2e6+100;
const ull base=39;
const ull mod=1e9+7;
char s[maxn],t[maxn];
ull hash_t[maxn],p[maxn];
int lens,lent,num0,num1;
bool solve(int s0)//表示0,1对应的字符串长度
{
int s1=(1ll*lent-(1ll*s0*num0))/num1;
ull H0=0,H1=0,B0=p[s0],B1=p[s1];
int set0=0,set1=0;
for(int i=1,j=1;i<=lens;i++){
if(s[i]=='0')
{
if(set0==0)
{
set0=1;
H0=(hash_t[j+s0-1]-hash_t[j-1]*B0%mod+mod)%mod,j+=s0;//debug(H0);
if(H1==H0&&set1==1) {return false;}
else continue;
}
ull temp_hash0=(hash_t[j+s0-1]-hash_t[j-1]*B0%mod+mod)%mod;
if(temp_hash0==H0)j+=s0;
else { return false;}
}
else if(s[i]=='1')
{
if(set1==0)
{
set1=1;
H1=(hash_t[j+s1-1]-hash_t[j-1]*B1%mod+mod)%mod,j+=s1;//debug(H1);
if(H1==H0&&set0==1){return false;}
else {continue;}
}
ull temp_hash1=(hash_t[j+s1-1]-hash_t[j-1]*B1%mod+mod)%mod;
if(temp_hash1==H1)j+=s1;
else return false;
}
}
return true;
}
int main()
{
scanf("%s%s",s+1,t+1);
lens=strlen(s+1),lent=strlen(t+1),num0=0,num1=0;
ull ans=0;
for(int i=1;i<=lens;i++){
if(s[i]=='0') num0++;
else num1++;
}
p[0]=1;
for(int i=1;i<=lent;i++) p[i]=p[i-1]*base%mod;
for(int i=1;i<=lent;i++) hash_t[i]=(hash_t[i-1]*base+t[i])%mod;
for(int i=1;1ll*i*num0+num1<=lent;i++)//枚举0对应字符串的长度
{
if((1ll*lent-(1ll*i*num0))%num1!=0) continue;
if(solve(i)) ans++;
}
printf("%llu",ans);
}
用板子写的多哈希版本:
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<':'<<x<<endl;
const int maxn=1e6+100;
struct Hash{
int base[4],mod[4];
int tot,hash[4][maxn],pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i
Hash() {
tot=0;
for(int i=1;i<=3;i++) pw[i][0]=1;
base[1]=233;base[2]=19260817;base[3]=20030714;
mod[1]=1e9+7;mod[2]=1e9+9;mod[3]=998244353;
}
void init() {
tot=0;
}
void insert(int c) {
tot++;
for(int i=1;i<=3;i++) hash[i][tot]=(1LL*hash[i][tot-1]*base[i]+c)%mod[i];
for(int i=1;i<=3;i++) pw[i][tot]=(1LL*pw[i][tot-1]*base[i])%mod[i];
}
//字符串[l,r]hash值,type为第几个hash
int query(int l,int r,int type) {
return (hash[type][r]-(1LL*hash[type][l-1]*pw[type][r-l+1]%mod[type])+mod[type])%mod[type];
}
//判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同
friend bool same(Hash &u,int lu,int ru,Hash &v,int lv,int rv) {
if(ru-lu!=rv-lv) return false;
for(int i=1;i<=3;i++) if(u.query(lu,ru,i)!=v.query(lv,rv,i)) return false;
return true;
}
}h;
char s[maxn],t[maxn];
int main()
{
h.init();
scanf("%s%s",s+1,t+1);int ans=0;
int num0=0,num1=0,lens=strlen(s+1),lent=strlen(t+1);
for(int i=1;i<=lent;i++) h.insert(t[i]);
if(s[1]=='1') for(int i=1;i<=lens;i++) s[i]=(s[i]=='0'?'1':'0');//'0'和‘1‘互换
for(int i=1;i<=lens;i++)
{
if(s[i]=='0')num0++;
else num1++;
}
for(int s0=1;1ll*s0*num0+num1<=lent;s0++)
{
bool flag=true;int start1=-1;//1对应的一个序列开始的位置;
if((lent-1ll*s0*num0)%num1!=0) continue;
int s1=(lent-1ll*s0*num0)/num1;
for(int i=1,j=1;i<=lens;i++)
{
if(s[i]=='0'){
if(!same(h,1,s0,h,j,j+s0-1)){flag=false;break;}
j+=s0;
}
else {
if(start1==-1) {start1=j;j+=s1; continue;}
else if(!same(h,start1,start1+s1-1,h,j,j+s1-1)){flag=false;break;}
j+=s1;
}
}
if(flag&&!same(h,start1,start1+s1-1,h,1,s0)) {ans++;}
}
printf("%d",ans);
}
压缩字符串: 1200E - Compress Words
题意:
经典看样例猜题意
tag:哈希,kmp裸题
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<':'<<x<<endl;
const int maxn=1e6+100;
struct Hash{
int base[4],mod[4];
int tot,hash[4][maxn],pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i
Hash() {
tot=0;
for(int i=1;i<=3;i++) pw[i][0]=1;
base[1]=233;base[2]=19260817;base[3]=20030714;//其余模数 39
mod[1]=1e9+7;mod[2]=1e9+9;mod[3]=998244353;
}
void init() {tot=0;}
void insert(int c) {
tot++;
for(int i=1;i<=3;i++) hash[i][tot]=(1LL*hash[i][tot-1]*base[i]+c)%mod[i];
for(int i=1;i<=3;i++) pw[i][tot]=(1LL*pw[i][tot-1]*base[i])%mod[i];
}
//字符串[l,r]hash值,type为第几个hash
int query(int l,int r,int type) {
return (hash[type][r]-(1LL*hash[type][l-1]*pw[type][r-l+1]%mod[type])+mod[type])%mod[type];
}
//判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同
friend bool same(Hash &u,int lu,int ru,Hash &v,int lv,int rv) {
if(ru-lu!=rv-lv) return false;
for(int i=1;i<=3;i++) if(u.query(lu,ru,i)!=v.query(lv,rv,i)) return false;
return true;
}
}ansh,h;
int main()
{
int n;cin>>n;string ans,s;
cin>>ans;ansh.init();
for(int i=0;i<ans.length();i++)ansh.insert(ans[i]);//稍微注意一下下标
for(int i=2;i<=n;i++)
{
cin>>s;int jmax=-1;h.init();//jmax设为-1,是为了考虑完全没匹配上的情况
for(int j=0;j<min(s.length(),ans.length());j++)
{
h.insert(s[j]);
if(same(ansh,(int)ans.size()-j,(int)ans.size(),h,1,j+1)) jmax=j;
}
jmax++;
ans+=s.substr(jmax,s.length()-jmax);
for(;jmax<s.length();jmax++)
ansh.insert(s[jmax]);
}
cout<<ans<<endl;
}
找包含三个串的串最短长度:E - Test
题意:给定三个字符串,求一个长度最短的字符串使这三个串是它的子串。
题解:跟上题相仿,但要先暴力判断是不是已经是子串了,然后枚举6种排列组合
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<':'<<x<<endl;
const int maxn=1e6+100;
struct Hash{
int base[4],mod[4];
int tot,hash[4][maxn],pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i
Hash() {
tot=0;
for(int i=1;i<=3;i++) pw[i][0]=1;
base[1]=233;base[2]=19260817;base[3]=20030714;//其余模数 39
mod[1]=1e9+7;mod[2]=1e9+9;mod[3]=998244353;
}
void init() {tot=0;}
void insert(int c) {
tot++;
for(int i=1;i<=3;i++) hash[i][tot]=(1LL*hash[i][tot-1]*base[i]+c)%mod[i];
for(int i=1;i<=3;i++) pw[i][tot]=(1LL*pw[i][tot-1]*base[i])%mod[i];
}
//字符串[l,r]hash值,type为第几个hash
int query(int l,int r,int type) {
return (hash[type][r]-(1LL*hash[type][l-1]*pw[type][r-l+1]%mod[type])+mod[type])%mod[type];
}
//判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同
friend bool same(Hash &u,int lu,int ru,Hash &v,int lv,int rv) {
if(ru-lu!=rv-lv) return false;
for(int i=1;i<=3;i++) if(u.query(lu,ru,i)!=v.query(lv,rv,i)) return false;
return true;
}
}ansh,h;
int solve(string s1,string s2,string s3)
{
string ans=s1;
ansh.init();
for(int i=0;i<ans.length();i++)ansh.insert(ans[i]);//稍微注意一下下标
int jmax=-1;h.init();//jmax设为-1,是为了考虑完全没匹配上的情况
for(int j=0;j<min(s2.length(),ans.length());j++)
{
h.insert(s2[j]);
if(same(ansh,(int)ans.size()-j,(int)ans.size(),h,1,j+1)) jmax=j;
}
jmax++;
ans+=s2.substr(jmax,s2.length()-jmax);
for(;jmax<s2.length();jmax++)
ansh.insert(s2[jmax]);
jmax=-1;h.init();//jmax设为-1,是为了考虑完全没匹配上的情况
for(int j=0;j<min(s3.length(),ans.length());j++)
{
h.insert(s3[j]);
if(same(ansh,(int)ans.size()-j,(int)ans.size(),h,1,j+1)) jmax=j;
}
jmax++;
ans+=s3.substr(jmax,s3.length()-jmax);
for(;jmax<s3.length();jmax++)
ansh.insert(s3[jmax]);
return ans.length();
}
string s[3];
bool cmp(string a,string b){return a.length()<b.length();}
bool in(string a,string b)//判断a是不是in b
{
for(int i=0;i<b.length()-a.length()+1;i++)
{
if(a==b.substr(i,a.length())) return true;
}
return false;
}
int main()
{
int ans=0x3f3f3f3f;
for(int i=0;i<3;i++)cin>>s[i];
sort(s,s+3,cmp);
if(in(s[1],s[2])) s[1]=s[2];
if(in(s[0],s[1])) s[0]=s[1];
if(in(s[0],s[2])) s[0]=s[2];
ans=min(ans,solve(s[0],s[1],s[2])); ans=min(ans,solve(s[0],s[2],s[1]));
ans=min(ans,solve(s[1],s[2],s[0])); ans=min(ans,solve(s[1],s[0],s[2]));
ans=min(ans,solve(s[2],s[0],s[1]));ans=min(ans,solve(s[2],s[1],s[0]));
cout<<ans<<endl;
}
找斐波那契数:HDU - 6768
题意:定义F[i]为斐波那契数列第i项,F[1]=1, F[2]=2, F[i]=F[i−1]+F[i−2] (i≥3)
已知任意正整数x都拥有一个唯一的长度为n的01数列b[n],使得
b[1]∗F[1]+b[2]∗F[2]+...+b[n]∗F[n]=x,bi∈{0,1}
以这样的表示法给出A、B、C三个数
已知数字C是由A∗B的结果在这样的表示法下将某个原本是1的位置改成0得来的
问抹去的是哪个位置
题解:哈希处理A,B,C,自然溢出,然后直接A*B-C,看对应哪个斐波那契数
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include<bits/stdc++.h>
using namespace std;
using namespace __gnu_pbds;
typedef unsigned long long ull;//自然溢出
gp_hash_table<ull,int>mp;
namespace IO
{
#define gc getchar()
template<typename T>inline void read(T &x) {
x=0;
char ch=gc;
while(ch<='9'&&ch>='0')
{
x=(x<<3)+(x<<1)+ch-'0';
ch=gc;
}
return;
}
}
ull fibo[2000050];
int main()
{
fibo[0]=fibo[1]=1;mp[1]=1;
for(int i=2;i<2000050;i++)
{
fibo[i]=fibo[i-1]+fibo[i-2];
mp[fibo[i]]=i;
}
int T;IO::read(T);
while(T--)
{
int cntA,cntB,cntC,d;
ull A=0,B=0,C=0;
IO::read(cntA);for(int i=1;i<=cntA;i++){IO::read(d); A+=d*fibo[i];}
IO::read(cntB);for(int i=1;i<=cntB;i++){IO::read(d); B+=d*fibo[i];}
IO::read(cntC);for(int i=1;i<=cntC;i++){IO::read(d); C+=d*fibo[i];}
printf("%d\n",mp[A*B-C]);
}
return 0;
}
二维哈希:POJ - 3690
题意:看样例猜题意,多组样例,第一行:N,M,T,P,Q
给一个NxM的矩阵,从中找有PxQ的那T个矩阵出现了多少个
Sample Input
3 3 2 2 2 *00 0** *00 ** 00 *0 ** 3 3 2 2 2 *00 0** *00 ** 00 *0 0* 0 0 0 0 0
Sample Output
Case 1: 1 Case 2: 2
#include<iostream>
#include<set>
#include<stdio.h>
using namespace std;
typedef unsigned long long ull;
const int MAX_SIZE = 1005;
const int MAX_T = 105;
int N,M,T,P,Q,test=0;
char field[MAX_SIZE][MAX_SIZE];
char patterns[MAX_T][MAX_SIZE][MAX_SIZE];
ull h[MAX_SIZE][MAX_SIZE],tmp[MAX_SIZE][MAX_SIZE];
const ull B1 = 9973;
const ull B2 = 100000007;
void compute_hash(char a[MAX_SIZE][MAX_SIZE],int n,int m){
//按行计算哈希值
ull t1=1;
for (int i = 0; i <Q ; ++i) t1*=B1;
for(int i=0;i<n;i++)
{
ull e=0;
for(int j=0;j<Q;j++)
e=e*B1+a[i][j];
tmp[i][0]=e;
for(int j=1;j+Q<=m;j++)
tmp[i][j]=tmp[i][j-1]*B1-a[i][j-1]*t1+a[i][j+Q-1];
}
ull t2=1;
for(int i=0;i<P;i++) t2*=B2;
for(int j=0;j<m-Q+1;j++)
{
ull e=0;
for(int i=0;i<P;i++)
e=e*B2+tmp[i][j];
h[0][j]=e;
for(int i=1;i+P<=n;i++)
h[i][j]=h[i-1][j]*B2-tmp[i-1][j]*t2+tmp[i+P-1][j];
}
}
void solve(){
multiset<ull> unseen;
for(int k=0;k<T;k++){
compute_hash(patterns[k],P,Q);
unseen.insert(h[0][0]);
}
compute_hash(field,N,M);
for(int i=0;i+P<=N;i++){
for(int j=0;j+Q<=M;j++){
unseen.erase(h[i][j]);
}
}
int ans = T - unseen.size();
printf("Case %d: %d\n",++test,ans);
}
int main(){
while(~scanf("%d %d %d %d %d", &N, &M, &T, &P, &Q)&&N&&M&&T&&P&&Q){
for(int i=0;i<N;i++) scanf("%s",field[i]);
for(int t=0;t<T;t++) for(int i=0;i<P;i++) scanf("%s",patterns[t][i]);
solve();
}
return 0;
}
复习二维哈希的时候补