1041 Be Unique (20 分) (hash散列)

博客介绍了火星独特的彩票中奖规则,即第一个押注唯一数字者获胜。给出了输入输出规格,包含正整数N及N个押注数字,需输出获胜数字,若无则输出None,并给出了示例输入输出。

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

Being unique is so important to people on Mars that even their lottery is designed in a unique way. The rule of winning is simple: one bets on a number chosen from [1,10

4

]. The first one who bets on a unique number wins. For example, if there are 7 people betting on { 5 31 5 88 67 88 17 }, then the second one who bets on 31 wins.

Input Specification:

Each input file contains one test case. Each case contains a line which begins with a positive integer N (≤10

5

) and then followed by N bets. The numbers are separated by a space.

Output Specification:

For each test case, print the winning number in a line. If there is no winner, print None instead.

Sample Input 1:

7 5 31 5 88 67 88 17

Sample Output 1:

31

Sample Input 2:

5 888 666 666 888 888

Sample Output 2:

None

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <cctype>
#include <string.h>
#include <cstdio>
using namespace std;
int a[100100],m[100100];
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
        m[a[i]]++;
    }
    for(int i=0;i<n;i++){
        if(m[a[i]]==1) {
            cout<<a[i];
            return 0;
        }
    }
    cout<<"None";
    return 0;
}

 

#ifndef _HASHSTRING_H_ #define _HASHSTRING_H_ #include <tuple> namespace INISTRING { typedef unsigned int uint32_t; class HashString { public: HashString() = delete;//默认构造函数, 禁止默认构造 HashString(const char* _szString);//带参构造函数 HashString(const HashString& _Instance)=delete;//删除复制构造函数, 无法复制 HashString(HashString&& _Instance)noexcept;//移动构造函数, std::move 触发 ~HashString();//析构函数 HashString& operator=(const HashString& _Instance)=delete;//删除重载赋值函数,无法赋值 HashString& operator=(HashString&& _Instance)noexcept;//重载赋值移动函数: std::move触发 bool operator==(const HashString& _Instance)const;//重载等于符号 bool operator>(const HashString& _Instance)const;//重载大于符号 bool operator<(const HashString& _Instance)const;//重载小于符号 HashString& operator=(const char* _szText);//重载赋值函数 const char* GetString()const;//获取字串首地址 const uint32_t GetLength()const;//获取字串长度 bool IsEmpty()const;//判断字串是否空字串 const unsigned long long Convert()const; private: //<const char* szHead, uint32_t m_iLength, uint32_t m_iHash1, uint32_t m_iHash1a> std::tuple<const char*, uint32_t, uint32_t, uint32_t> CalcString(const char* _szText); uint32_t FNVHash1(const char* _szText, uint32_t _iLength); uint32_t FNVHash1a(const char* _szText, uint32_t _iLength); private: char *m_szText; uint32_t m_iLength; //采用双重hash验证, hash双重算法使用FNV-1和FNV-1a struct HashValue { uint32_t m_iHash1a; uint32_t m_iHash1; }; HashValue m_vecHash; unsigned long long* m_pHash;//指向m_vecHash内存块的指针,用来快速比较 }; } #include "HashString-inl.h" #endif#include "HashString.h" #ifdef _DEBUG //#define _INISTRING_DEBUG_ #endif // DEBUG #ifdef _INISTRING_DEBUG_ #include "../../DebugInfo/Include/DebugInfo.h" #endif // _INISTRING_DEBUG_ inline bool IsSpace(const unsigned char* _pByte); /* { return\ *_pByte == ' ' || *_pByte == '\t' || *_pByte == '\n' || *_pByte == '\v' || *_pByte == '\f' || *_pByte == '\r'; }*/ /* # FNV - 1算法公式 hash = FNV_offset_basis for each byte_of_data to be hashed hash = hash * FNV_prime hash = hash ^ byte_of_data return hash # FNV - 1a算法公式 hash = FNV_offset_basis for each byte_of_data to be hashed hash = hash ^ byte_of_data hash = hash * FNV_prime return hash */ inline uint32_t CalcHash1(const unsigned char _Byte, uint32_t _iOffset, const uint32_t _iFNVPrime = 16777619) { _iOffset *= _iFNVPrime; _iOffset ^= _Byte; return _iOffset; } inline uint32_t CalcHash1a(const unsigned char _Byte, uint32_t _iOffset, const uint32_t _iFNVPrime = 16777619) { _iOffset ^= _Byte; _iOffset *= _iFNVPrime; return _iOffset; } uint32_t INISTRING::HashString::FNVHash1(const char* _szText, uint32_t _iLength) { uint32_t iHash = 2166136261;//初始化hash 32位的填充值 uint32_t iFNVPrime = 16777619;//FNV的32位的散列因子? for (unsigned int i = 0; i < _iLength; ++i) { iHash *= iFNVPrime; iHash ^= _szText[i]; } return iHash; } uint32_t INISTRING::HashString::FNVHash1a(const char* _szText, uint32_t _iLength) { uint32_t iHash = 2166136261;//初始化hash 32位的填充值 uint32_t iFNVPrime = 16777619;//FNV的32位的散列因子? for (unsigned int i = 0; i < _iLength; ++i) { iHash ^= _szText[i]; iHash *= iFNVPrime; } return iHash; } std::tuple<const char*, uint32_t, uint32_t, uint32_t> INISTRING::HashString::CalcString(const char* _szText) { //放弃优化,因为key的长度一般很短。优化复杂度如下: //1. 过滤字串头部和尾部的空格 //2. 计算字串的长度(已经过滤头尾空格) //3. 计算字串的hash值 //如果要优化,复杂度代价可能比直接while逐个解析char的代价还要大 //register:这个关键字请求编译器尽可能的将变量存在CPU内部寄存器中 //而不是通过内存寻址访问,以提高效率。注意是尽可能,不是绝对。 //毕竟cpu寄存器有限 //解析过程: 每次抽4byte逐个解析, 因为提供的字串流可能是存在于硬盘。 //存在于硬盘, 每次抽4byte到内存 比起 每次抽1byte到内存 可能更快 const char* szHead = nullptr;//过滤空格后的字串头地址,用作返回值 const char* szTail = nullptr;//过滤空格后的字串尾地址 const char* szSource = _szText;//目标字串操作指针 register uint32_t iMagicBits = 0x7EFEFEFFL;//cpu进行位运算所需的因子数,计算4byte里面是否含有空字符'0' register uint32_t iBlock;//抽出来的4byte配合MagicBits进行位运算,快速判断4byte里面是否含有空字符'0' unsigned char vecBuffer[sizeof(uint32_t)];//抽出4byte的储存容器 //计算hash的思路,采用极端情况的字串: " string text " ,此字串头尾和中间都有空格。并且需要过滤掉头尾空白字符的同时计算出hash值 //1.开始计算hash肯定跳过字串头部的空格 //2.字串中间的空格只能当作临时hash计算,因为你不知道这个空格后面会不会都只有空格(此空格后面可能还有有效字符)。此时计算空格后的hash存放在iTmpHash内 //3.iHash所存放的都是最一个非空格字符的hash,这样即使在字串中间遇到空格字符,也能照常向下计算hash(计算所得放在iTmpHash),因为如果之后的字符全是空格的话,此时iHash就是最终的hash了,而iTmpHash就永远被放弃了 //处理hash值使用的数据, FNV1版本 uint32_t iHash1 = 0;//储存最终hash值的容器 uint32_t iTmpHash1 = 2166136261;//储存临时hash值的容器,并填充初始值 //处理hash值使用的数据, FNV1a版本 uint32_t iHash1a = 0;//储存最终hash值的容器 uint32_t iTmpHash1a = 2166136261;//储存临时hash值的容器,并填充初始值 while (true) { *(uint32_t*)vecBuffer = *(uint32_t*)szSource;//从字串流中抽取4byte到容器 iBlock = *(uint32_t*)vecBuffer;//把抽出来的4byte将由cpu计算 if ((((iBlock + iMagicBits) ^ ~iBlock) & ~iMagicBits) != 0)//计算4byte里面是否含有空字符'0' {//含有空字符'0' for (int i = 0; i < sizeof(uint32_t); ++i) { //找到字串末尾,打包并返回所需数据 if (vecBuffer[i] == '\0') { return std::make_tuple(szHead, szTail - szHead, iHash1, iHash1a); } if (IsSpace(vecBuffer + i))//如果是空白字符 { //字串头地址已经找到,此空白字符开始计算hash值。并储存在临时hash容器 if (szHead) { iTmpHash1 = CalcHash1(vecBuffer[i], iTmpHash1);//FNV1版本 iTmpHash1a = CalcHash1a(vecBuffer[i], iTmpHash1a);//FNV1a版本 } //else 字串头都未找到,说明正在过滤字串头部空格 } else//如果不是空白字符 { if (!szHead)szHead = szSource + i;//找到字串头地址 szTail = (szSource + i) + 1;//计算字串尾地址 //FNV1版本 iTmpHash1 = CalcHash1(vecBuffer[i], iTmpHash1);//计算hash iHash1 = iTmpHash1;//此字符非空白字符,临时hash值转交给最终hash值 //FNV1a版本 iTmpHash1a = CalcHash1a(vecBuffer[i], iTmpHash1a);//计算hash iHash1a = iTmpHash1a;//此字符非空白字符,临时hash值转交给最终hash值 } } } else {//不含空字符 for (int i = 0; i < sizeof(uint32_t); ++i) { if (IsSpace(vecBuffer + i))//如果是空白字符 { //字串头地址已经找到,此空白字符开始计算hash值。并储存在临时hash容器 if (szHead) { iTmpHash1 = CalcHash1(vecBuffer[i], iTmpHash1);//FNV1版本 iTmpHash1a = CalcHash1a(vecBuffer[i], iTmpHash1a);//FNV1a版本 } //else 字串头都未找到,说明正在过滤字串头部空格 } else//如果不是空白字符 { if (!szHead)szHead = szSource + i;//找到字串头地址 szTail = (szSource + i) + 1;//计算字串尾地址 //FNV1版本 iTmpHash1 = CalcHash1(vecBuffer[i], iTmpHash1);//计算hash iHash1 = iTmpHash1;//此字符非空白字符,临时hash值转交给最终hash值 //FNV1a版本 iTmpHash1a = CalcHash1a(vecBuffer[i], iTmpHash1a);//计算hash iHash1a = iTmpHash1a;//此字符非空白字符,临时hash值转交给最终hash值 } } } szSource += sizeof(uint32_t); } } /* //默认构造函数 INISTRING::HashString::HashString():m_szText(nullptr),m_iLength(0),m_vecHash{0}, m_pHash((long long*)(&m_vecHash)) { #ifdef _INISTRING_DEBUG_ Info("默认构造: 创建新HashString空白对象<{}>", fmt::ptr(this)); #endif // _INISTRING_DEBUG_ } */ //带参<char*>构造函数 INISTRING::HashString::HashString(const char* _szText) : m_szText(nullptr), m_iLength(0),m_vecHash{0}, m_pHash((unsigned long long*)(&m_vecHash)) { #ifdef _INISTRING_DEBUG_ Info("带参构造<char*>: 创建新HashString对象<{}>", fmt::ptr(this)); if (!_szText)Error("带参构造<char*>:不能使用空指针初始化HashString:{}", fmt::ptr(this)); assert(_szText != 0 && "不能使用空指针初始化HashString"); #endif // _INISTRING_DEBUG_ const char* szHead = nullptr;//过滤空格后的字串头地址,用作返回值 std::tie(szHead, m_iLength, m_vecHash.m_iHash1, m_vecHash.m_iHash1a) = CalcString(_szText);//计算字串,并解包返回值 //uint32_t iHash1 = FNVHash1(szHead, m_iLength);//调试对比 //uint32_t iHash1a = FNVHash1a(szHead, m_iLength);//调试对比 if (m_iLength) { uint32_t iLength = (sizeof(uint32_t) - (m_iLength % sizeof(uint32_t))) + m_iLength;//求对齐数 m_szText = new char[iLength];//创建内存对齐的内存块 memcpy(m_szText, szHead, m_iLength);//复制字串到内存块 m_szText[m_iLength] = '\0';//强制帮字串末尾加入空字符 #ifdef _INISTRING_DEBUG_ Info("字串内存块地址:{},内容<{:s}>", fmt::ptr(m_szText), m_szText); #endif } #ifdef _INISTRING_DEBUG_ else Warn("带参构造<char*>: 创建新HashString对象<{}>, 目标是空字串", fmt::ptr(this)); #endif // _INISTRING_DEBUG_ } //移动构造函数, std::move 触发 INISTRING::HashString::HashString(HashString&& _Instance)noexcept: m_szText(nullptr), m_iLength(0), m_vecHash{ 0 }, m_pHash((unsigned long long*)(&m_vecHash)) { #ifdef _INISTRING_DEBUG_ Info("HashString对象<{}>触发std::move, move对象<{}>", fmt::ptr(this), fmt::ptr(&_Instance)); #endif // _INISTRING_DEBUG_ *this = std::forward<HashString>(_Instance);//完美转发右值引用 } //重载赋值移动函数: std::move触发 INISTRING::HashString& INISTRING::HashString::operator=(HashString&& _Instance)noexcept { if (m_szText) { #ifdef _INISTRING_DEBUG_ Info("HashString对象<{}>移动赋值, 正在销毁字串内存块<{}>", fmt::ptr(this), fmt::ptr(m_szText)); #endif // _INISTRING_DEBUG_ delete m_szText; } m_szText = _Instance.m_szText; m_iLength = _Instance.m_iLength; m_vecHash = _Instance.m_vecHash; _Instance.m_szText = nullptr; _Instance.m_iLength = 0; _Instance.m_vecHash.m_iHash1 = 0; _Instance.m_vecHash.m_iHash1a = 0; return *this; } //析构函数 INISTRING::HashString::~HashString() { #ifdef _INISTRING_DEBUG_ Info("析构HashString对象,对象地址:{}, 正在销毁字串内存块<{}>", fmt::ptr(this), fmt::ptr(m_szText)); #endif // _INISTRING_DEBUG_ if (m_szText)delete m_szText; } //重载比较 等于函数 bool INISTRING::HashString::operator==(const HashString& _Instance)const { return *m_pHash == *_Instance.m_pHash; } //重载比较 大于函数 bool INISTRING::HashString::operator>(const HashString& _Instance)const { return *m_pHash > *_Instance.m_pHash; } //重载比较 小于函数 bool INISTRING::HashString::operator<(const HashString& _Instance)const { return *m_pHash < *_Instance.m_pHash; } //重载赋值函数 INISTRING::HashString& INISTRING::HashString::operator=(const char* _szText) { #ifdef _INISTRING_DEBUG_ Info("HashString对象<{}>触发重载赋值函数", fmt::ptr(this)); #endif // _INISTRING_DEBUG_ * this = HashString(_szText); return *this; } //获取字串首地址 const char* INISTRING::HashString::GetString()const { return m_szText; } //获取字串长度 const uint32_t INISTRING::HashString::GetLength()const { return m_iLength; } //判断字串是否空字串 bool INISTRING::HashString::IsEmpty()const { return m_iLength ? true : false; } const unsigned long long INISTRING::HashString::Convert()const { return *m_pHash; } 根据我提供的代码,帮我优先它,但不要更改类名
最新发布
07-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小火汁猛猛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值