hash——雪花雪花雪花(hash解法,最小表示+hash解法)

传送门:137. 雪花雪花雪花 - AcWing题库

思路:这里相同的雪花一定是六个角的长度之和,长度之积都相同,就想一般的数字hash中的余数一样,只有余数相同才能去找数字是否存在。这里用的拉链法来做。

哈希表暴力代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
using namespace std;
const int N=1e5+10,mod=1e5+3;
int snow[N][6];
int h[N],e[N],ne[N],idx;//因为是以邻接表的形式存,不需要开数倍
int n;

int hh(int a[])//获取该雪花在哪条链表中
{
    int sum=0,mul=1;
    for(int i=0;i<6;i++)
    {
        sum=(sum+a[i])%mod;
        mul=(long long)mul*a[i]%mod;
    }
    return (mul+sum)%mod;
}
bool equal(int a[],int b[])//判断两篇雪花是否相同
{

    for(int i=0;i<6;i++)
        for(int j=0;j<6;j++)
    {
     bool flag=1;
     for(int k=0;k<6;k++)
        if(a[(i+k)%6]!=b[(j+k)%6]) flag=0;
      if(flag)  return 1;

      flag=1;
      for(int k=0;k<6;k++)
        if(a[(i+k)%6]!=b[(j-k+6)%6]) flag=0;
      if(flag) return 1;
    }
    return 0;
}
bool insert(int a[])//询问或插入一片新雪花
{
    int t=hh(a);
    for(int i=h[t];i;i=ne[i])
    {
        if(equal(snow[i],a))return 1;
    }

    idx++;//这里会从1开始,所以h数组不用初始化为-1.
    memcpy(snow[idx],a,6*sizeof(int));
    ne[idx]=h[t];
    h[t]=idx;
    return 0;
}
int main()
{
    cin>>n;
   // memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++)
    {
        int a[8];
        for(int j=0;j<6;j++) scanf("%d",&a[j]);
        if(insert(a))
        {
            puts("Twin snowflakes found.");
            return 0;
        }
    }
    puts("No two snowflakes are alike.");
        return 0;
}

最小表示法+哈希表代码:

思路:将每一个字符串正向反向各求一次最小表示,求出哈希值,用stl中的哈希表来存,当有两个相同的哈希值时说明有两片一模一样的雪花。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_set>
using namespace std;
typedef unsigned long long ull;
const int N=1e5+10,p=131;
int n;
int a[N][13];
int b[N][13];
int left1[N],right1[N];
unordered_set<ull>s;
int get_min(int s[])
{
    int i=1,j=2,k;
    while(i<=6&&j<=6)
    {
        for(k=0;k<6&&s[i+k]==s[j+k];k++);
        if(k==6)break;
        if(s[i+k]>s[j+k])
        {
            i=i+k+1;
            if(i==j) i++;
        }else
        {
            j=j+k+1;
            if(i==j) j++;
        }
    }
    return min(i,j);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
       for(int j=1;j<=6;j++)
       {
           scanf("%d",&a[i][j]);
           a[i][j+6]=a[i][j];
       }
       left1[i]=get_min(a[i]);
    }

    memcpy(b,a,sizeof a);

    for(int i=1;i<=n;i++)
    {
        reverse(b[i]+1,b[i]+13);//存反向字符串的最小表示
        right1[i]=get_min(b[i]);
    }

    bool flag=0;
    for(int i=1;i<=n;i++)
    {
        ull sum1=0,sum2=0;
        for(int j=0;j<6;j++)
        {
            sum1=sum1*p+a[i][left1[i]+j];
            sum2=sum2*p+b[i][right1[i]+j];
        }
        if(s.count(sum1)!=0)
        {
            flag=true;
            break;
        }else s.insert(sum1);

        if(sum1!=sum2)
        {
            if(s.count(sum2)!=0)
        {
            flag=true;
            break;
        }else s.insert(sum2);

        }
    }
    if(flag)puts("Twin snowflakes found.");
    else puts("No two snowflakes are alike.");

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值