字符串哈希
对于字符串的处理,有很多需要把重复字符串去掉的问题,但是直接比较字符串会出现很多问题。
例如,我将 "abc","bbb","cba",直接sb的变成数字进行比较,会导致将这三个字符串判断为一样的(233)
一般智商正常情况下 , 直接比较 判重 需要将字符串一个一个比对, 所以就会浪费大量时间。
这个时候我们就需要将字符串转化为一定大小的数字来进行储存。这就需要我们来使用hash。
那么哈希的原理是什么呢?哈希的原理就是将·字符串中每一个字符乘上一个数字(像进制转换那样搞成一个数)(一般是一个质数,具体原因我也不清楚,希望有dalao能在评论里指出来。)
就是把字符串搞成一个很好的数字放到bool数组里进行判重,
#include<bits/stdc++.h> using namespace std; #define X 127 const int N=10005; typedef unsigned long long Ull; int n,ans; Ull a[N]; char s[N]; int HASH(){ scanf("%s",s+1); int len=strlen(s+1); int Hash=0; for(int i=1;i<=len;i++){ Hash=Hash*X+s[i]-'a',Hash%=127; } return Hash; }
至于为什么用unsigned long long 来储存是为了防止字符串太大溢出,使用unsigned long long可以在溢出的情况下大概率保证hash值不同(还是有非酋会撞上相同的情况)。
然后就可以利用hash值来对比两个字符串是否相等。
附上几道hash水题
洛谷3370 模板题
#include<bits/stdc++.h> using namespace std; #define X 127 const int N=10005; typedef unsigned long long Ull; int n,ans; Ull a[N]; char s[N]; int HASH(){ scanf("%s",s+1); int len=strlen(s+1); int Hash=0; for(int i=1;i<=len;i++){ Hash=Hash*X+s[i]-'a'; } return Hash; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ a[i]=HASH(); } 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; }
codevs 2875
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define X 127 #define mod 999991 bool a[mod+6]; int n,m; int Hash(){ char s[1100]; scanf("%s",s+1); int tot=0; int len=strlen(s+1); for(int i=1;i<=len;i++){ tot=(tot*X+s[i]-'a')%mod; } return (tot%mod); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ a[Hash()]=1; } scanf("%d",&m); for(int i=1;i<=m;i++){ if(a[Hash()])printf("Yes\n"); else printf("No\n"); } return 0; }
还有一道bzoj水题 可以练习hash和平衡树
bzoj 2761
hash做法
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define mod 1999 #define N 50010 bool Hash[N]; int cnt,a[N],to[N],n; int head[N]; inline bool judge(int x){ if(!Hash[x])return 0; for(int i=head[x];i;i=to[i]){ if(a[i]==x)return 1; } return 0; } inline void insert(int x){ Hash[x]=1; a[++cnt]=x; to[cnt]=head[x]; head[x]=cnt; } int main(){ int T; scanf("%d",&T); while(T--){ cnt=0; memset(head,0,sizeof(head)); memset(to,0,sizeof(to)); memset(a,0,sizeof(a)); scanf("%d",&n); int x=0; for(int i=1;i<=n;i++){ scanf("%d",&x); if(judge(x))continue; insert(x); if(i==1)printf("%d",x); else printf(" %d",x); } printf("\n"); } return 0; } /* 2 11 1 2 18 3 3 19 2 3 6 5 4 6 1 2 3 4 5 6 */
当我们计算两个不同的字符串 有可能凑巧他们的hash值是相等的 ,于是就可以每个字符串算出两个hash值
附上恶心的双hash代码(看一看找个乐子就行了 ,这种恶心的代码除了J1m没人会写的,还是去学STLmap吧)
//输入+双哈希 for(int k=1;k<=N;k++,M++) { char s1[12],s2[12]; scanf("%s %s",s1,s2); long long h3=0,h4=0; for(int i=0;i<strlen(s1);i++)h3=(h3*Jin+s1[i])%mod,h4=(h4*Kai+s1[i])%mod; if(size[h3]) for(int i=1;i<=size[h3]+1;i++) { if(hash[h3][i][0]==h4){x[M]=hash[h3][i][1];break;} if(i==size[h3]+1){size[h3]++,hash[h3][i][0]=h4,hash[h3][i][1]=++cnt,x[M]=cnt;break;} } else size[h3]++,hash[h3][1][0]=h4,hash[h3][1][1]=++cnt,x[M]=cnt;