水水的哈希函数题解

本文介绍了几种典型问题的哈希表实现方法,包括数字频率统计、寻找特定数值组合及正方形点集计数等问题,通过具体示例展示了哈希表在解决实际问题中的高效性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.hdu1800 Flying to the Mars
题意:给你n个数,输出出现最多次数数的次数。
思路:由于每个数长度小于30,需要对这些数用字符串读入后,用哈希函数对应一个值,开放地址法解决冲突。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int N=1000005;
int inf=0x3f3f3f3f;
const int maxn=7003;
int Hash[maxn],Count[maxn],n,maxit;
LL mod=1e9+7;
inline int ELFhash(char *key)
{
   unsigned long h=0;
   while(*key)
   {
       h=(h<<4)+*key++;
       unsigned long g=h&0xf0000000L;
       if(g)
       h^=g>>24;
       h&=~g;
   }
   return h%N;//%N?o
}

inline void hashit(char *s)
{
    int k;
    while(*s=='0') s++;
    k=ELFhash(s);
    int t=k%maxn;
    while(Hash[t]!=k&&Hash[t]!=-1)
        t=(t+1)%maxn;
    if(Hash[t]==-1){
        Count[t]=1,Hash[t]=k;
    }
    else if(++Count[t]>maxit)
        maxit=Count[t];
    return;
}

int main()
{
     int n;
     char str[100];
     while(~scanf("%d",&n))
     {
         memset(Hash,-1,sizeof(Hash));
         memset(Count,0,sizeof(Count));
         getchar();
         maxit=1;
         for(int i=0;i<n;i++)
         {
             gets(str);
             hashit(str);
         }
         cout<<maxit<<endl;
     }
     return 0;
}

醉了…说好数字长度30,直接map也能过…

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        map<int,int>mp;
        int maxx=0;
        for(int i=0;i<n;i++)
        {
            int d;scanf("%d",&d);
            mp[d]++;
            maxx=max(maxx,mp[d]);
        }
        printf("%d\n",maxx);
    }
}

2.poj2549 Sumsets
题意:给n个数,找出一个最大的数d,满足a+b+c=d.a,b,c,d都是这n个数里的。这里n<1000,-536870912

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define mm 100003
using namespace std;
bool flag;
int ha[mm],next[mm],s[1010],res,n;
struct Node
{
    int x,y;
}node[500510];

void solve(int xx,int yy)
{
    int key=(xx-yy<0?yy-xx:xx-yy)%mm;
    int i=ha[key];
    while(i!=-1)
    {
        //cout<<node[i].x<<node[i].y<<xx<<yy<<endl;
        if((node[i].x+node[i].y)==(xx-yy)&&node[i].x!=xx&&node[i].x!=yy&&node[i].y!=xx&&node[i].y!=yy)
        {
            res=max(res,xx);
            return;
        }
        i=next[i];
    }
    return;
}

int main()
{
    while(scanf("%d",&n)&&n)
    {
        memset(ha,-1,sizeof(ha));
       for(int i=0;i<n;i++)scanf("%d",&s[i]);
       int c=0;sort(s,s+n);
       for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
       {

          node[c].x=s[i],node[c].y=s[j];
          int key=(s[i]+s[j]<0?-s[j]-s[i]:s[i]+s[j])%mm;
          next[c]=ha[key];
          ha[key]=c;
          c++;
       }
       res=-1000000000;
       for(int i=n-1;i>=0;i--)
        for(int j=n-1;j>=0;j--)
       {
           if(i==j) continue;
           solve(s[i],s[j]);
       }
       if(res!=-1000000000) cout<<res<<endl;
       else cout<<"no solution"<<endl;
    }
}

这题还有一个很经典做法,中途相遇法,先排个序,枚举d-c,在剩下的搜索a+b,用的中途相遇法,很巧妙。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int inf=-600000000;
int a[1001];
int main()
{
    int n;
    while(scanf("%d",&n)&&n)
    {
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    sort(a,a+n);
    n--;
    int ans=inf;
    for(int i=n;i>=0;i--)
    {
        for(int j=n;j>=0;j--)
        {
        if(i==j)
            continue;
        int sum=a[i]-a[j];
        for(int l=0,r=j-1;l<r;)
        {
            if(a[l]+a[r]==sum)
            {
            ans=a[i];
            break;
            }
            if(a[l]+a[r]>sum)
            r--;
            else
            l++;
        }
        if(ans!=inf)
            break;
        }
        if(ans!=inf)
        break;
    }
    if(ans==inf)
        printf("no solution\n");
    else
        printf("%d\n",ans);
    }
    return 0;
}

3.HDU1496Equations
题目大意,给你a,b,c,d这4个数的值,然后问a*x1^2 + b*x2^2 + c*x3^2 + d*x4^2 = 0
的(x1,x2,x3,x4)解一共有多少种?
直接暴力n^4,n=1000,绝对TLE ,加上数据范围50*100000*2=100w可以开这么大的数组,所以直接hash表,很巧妙!分开两部分求和,结果正负用两个数组区别开,若两部分的和是0,则就加上那么多种,由于每个地方都可以正取负,最后乘以2^4.

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int maxn=50*100*100*2+100;
int inf=0x3f3f3f3f;
LL mod=1e9+7;
int hash_z[maxn],hash_f[maxn],res[101];
int main()
{
    int a,b,c,d,sum;
    for(int i=0;i<=100;i++) res[i]=i*i;
    while(~scanf("%d %d %d %d",&a,&b,&c,&d))
    {
        memset(hash_z,0,sizeof(hash_z));
        memset(hash_f,0,sizeof(hash_f));
        if((a>0&&b>0&&c>0&&d>0)||(a<0&&b<0&&c<0&&d<0))
        {
            printf("0\n");
            continue;
        }
        for(int i=1;i<=100;i++)
        {
            for(int j=1;j<=100;j++)
            {
               sum=a*res[i]+b*res[j];
               if(sum>=0) hash_z[sum]++;
               else hash_f[-sum]++;
            }
        }
        int t=0;
        for(int i=1;i<=100;i++)
        {
            for(int j=1;j<=100;j++)
            {
                sum=c*res[i]+d*res[j];
                if(sum>0) t+=hash_f[sum];
                else t+=hash_z[-sum];
            }
        }
        printf("%d\n",t<<4);
    }
}

大神精炼的代码,直接开了200w数组,加一个基值就不用考虑正负问题,也能一一对应。

#include"stdio.h"
#include"string.h"
#include"stdlib.h"
int hash[2000011];
int main()
{
    int i,l;
    int a,b,c,d;
    int ans;
    while(scanf("%d%d%d%d",&a,&b,&c,&d)!=-1)
    {
        if(a*b>0 && b*c>0 && c*d>0) {printf("0\n");continue;}
        memset(hash,0,sizeof(hash));
        for(i=1;i<=100;i++)
        for(l=1;l<=100;l++)
            hash[a*i*i+b*l*l+1000000]++;
        ans=0;
        for(i=1;i<=100;i++)
        for(l=1;l<=100;l++)
            ans+=hash[-c*i*i-d*l*l+1000000];
        printf("%d\n",16*ans);
    }
    return 0;
}

4 poj 2503
题意:输入10w个字典词汇。再输入字典词汇对应的方言,问这个词汇是否存在字典里,存在输出,不存在就输出“eh”.
题意很好理解,就是输入数据10w个,所以必须用字符串hash解决,推荐使用ELFhash函数,拉链发解决冲突,也可以保存后,用二分查找。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
#include<iterator>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int maxn=100003;
int inf=0x3f3f3f3f;
int ELFhash(char *key)
{
    unsigned long h=0;
    while(*key){
        h=(h<<4)+*key++;
        unsigned long g=h&0Xf0000000L;
        if(g)
            h^=g>>24;
        h&=~g;
    }
    return h%maxn;

}
int ha[maxn],next[maxn];
struct Node
{
    char str1[15],str2[15];
}node[maxn];

int main()
{
    char s[30];
    int n=0;
    memset(ha,-1,sizeof(ha));
    while(gets(s))
    {
        if(sscanf(s,"%s%s",node[n].str1,node[n].str2)!=2)
            break;
        else
        {
            int key=ELFhash(node[n].str2);
            next[n]=ha[key];
            ha[key]=n;
            n++;
        }
    }
    while(~scanf("%s",s))
    {
        int key=ELFhash(s);
        int i=ha[key];
        while(i!=-1)
        {
            if(strcmp(s,node[i].str2)==0)
                break;
            i=next[i];
        }
        if(i==-1) printf("eh\n");
        else printf("%s\n",node[i].str1);
    }
}

二分

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<iostream>
const int maxn=1000010;
using namespace std;
typedef struct Str
{
    char f[20],e[20];
}STR;
STR str[maxn];
int t=0;

int cmp(const void *a, const void *b)
{
    return strcmp((*(STR *)b).e, (*(STR *)a).e);
}
int binary_Search(char *s)
{
    int l=0,r=t-1;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(strcmp(s,str[mid].e)==0)
            return mid;
        else if(strcmp(s,str[mid].e)>0)  r=mid-1;
        else l=mid+1;
    }
    return -1;
}

int main()
{
    char s[30];
    while(gets(s))
    {
        if(s[0]=='\0') break;
        sscanf(s,"%s %s",str[t].f,str[t].e);
        t++;
    }
    qsort(str,t,sizeof(STR),cmp);
    while(gets(s))
    {
        int index=binary_Search(s);
        if(index==-1) printf("eh\n");
        else printf("%s\n",str[index].f);
    }
    return 0;
}

5.poj2002 Squares
题意:给你n个二维平面的点,问这些点能够成多少个不同的正方形。
思路:这题难点需要很好的数学功底:如何在二维平面坐标里,已知两点坐标,求可能构成的正方形的另两个点坐标,需要用到向量的旋转公式,或者证明下图三角形全等。
这里写图片描述
可以得到
**已知: (x1,y1) (x2,y2)
则: x3=x1+(y1-y2) y3= y1-(x1-x2) ,x4=x2+(y1-y2) y4= y2-(x1-x2)
或x3=x1-(y1-y2) y3= y1+(x1-x2) x4=x2-(y1-y2) y4= y2+(x1-x2)**
接下来就是,将每个点平方取余后进入哈希表,每两个点枚举,求得另两个点后在哈希表里查找。最后注意,这样求得是正方形每个边都计数了一次,导致重复,最后结果/4.

#include<map>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=1010;
const int prime=1007;
int inf=0x3f3f3f3f;
ll mod=1e9+7;
int Hash[maxn],n;
struct Node
{
    int x,y,next;
}node[maxn];

bool find_point(int a,int b)
{
    int key=(a*a+b*b)%prime;
    int i=Hash[key];
    while(i!=-1)
    {
        if(node[i].x==a&&node[i].y==b) return true;
        i=node[i].next;
    }
    return false;
}

int main()
{
    while(scanf("%d",&n)&&n)
    {
        int ans=0;
        memset(Hash,-1,sizeof(Hash));
        for(int i=0;i<n;i++)
        {
            int a,b;scanf("%d %d",&a,&b);
            int key=(a*a+b*b)%prime;
            node[i].x=a,node[i].y=b;
            node[i].next=Hash[key];
            Hash[key]=i;
        }
        for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++)
        {
            int x3=node[i].x+(node[i].y-node[j].y);
            int y3=node[i].y-(node[i].x-node[j].x);
            int x4=node[j].x+(node[i].y-node[j].y);
            int y4=node[j].y-(node[i].x-node[j].x);
            if(find_point(x3,y3)&&find_point(x4,y4)) ans++;
        }
          for(int i=0;i<n;i++)
            for(int j=i+1;j<n;j++)
        {
            int x3=node[i].x-(node[i].y-node[j].y);
            int y3=node[i].y+(node[i].x-node[j].x);
            int x4=node[j].x-(node[i].y-node[j].y);
            int y4=node[j].y+(node[i].x-node[j].x);
            if(find_point(x3,y3)&&find_point(x4,y4)) ans++;
        }
        printf("%d\n",ans>>2);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值