力扣官网 11 22
暴雪MPQ算法
//转自 https://blog.youkuaiyun.com/dwb1015/article/details/21008329
//暴雪的mpq技术
//我在个人机器上实现过4000万条字符串存储,碰撞很小
#include <iostream>
using namespace std;
#define nTableSize 40000000
#define nMaxStrLen 20
unsigned long cryptTable[0x1000];
typedef struct _MPQHASHTABLE
{
char bExists;
}MPQHASHTABLE;
MPQHASHTABLE HashTable[nTableSize];
int HashATable[nTableSize];
int HashBTable[nTableSize];
char data[nTableSize][nMaxStrLen];
class CHashForMpq
{
public:
int insert_string(const char *string_in)
{
const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;//定义哈希类型
/*计算lpszString字符串经过解析后应该放入或者查询各表的数据*/
unsigned int nHash = HashString(string_in, HASH_OFFSET);//生成要放入hash表中的数据
unsigned int nHashA = HashString(string_in, HASH_A);//要放入hashA表中的数据
unsigned int nHashB = HashString(string_in, HASH_B);//要放入hashB表中的数据
unsigned int nHashStart = nHash % nTableSize;//计算起始查询位置
unsigned int nHashPos = nHashStart;//初始化实际查询到的位置
while (HashTable[nHashPos].bExists)//发生了碰撞
{
//校验碰撞原因
if (HashATable[nHashPos] == nHashA && HashBTable[nHashPos] == nHashB)
break; //原始数据发生了重复,可能的情况:1.发生了错误,2.我们执行的是查询
else
nHashPos = (nHashPos + 1) % nTableSize;//说明数据没有重复,但原来的位置上有数据,也就是发生了碰撞的情况,所以将实际位置偏移
if (nHashPos == nHashStart)
{
cout<<"表已满,无法插入数据"<<endl;
return 0;
}
}
/*插入的情况*/
if (!HashTable[nHashPos].bExists && (strlen(string_in) < nMaxStrLen))
{
HashATable[nHashPos] = nHashA;
HashBTable[nHashPos] = nHashB;
strcpy(data[nHashPos], string_in);
HashTable[nHashPos].bExists = 1;
cout<<"字符串"<<string_in<<"插入成功,位于"<<nHashPos<<endl;
}
else
{
if(HashTable[nHashPos].bExists)
cout<<"字符串"<<string_in<<"已存在于表中,无法插入"<<endl;
else
cout<<"字符串"<<string_in<<"长度超标,无法插入"<<endl;
}
return nHashPos;
}
int search_string(const char *string_in)
{
const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;//定义哈希类型
/*计算lpszString字符串经过解析后应该放入或者查询各表的数据*/
unsigned int nHash = HashString(string_in, HASH_OFFSET);//生成要放入hash表中的数据
unsigned int nHashA = HashString(string_in, HASH_A);//要放入hashA表中的数据
unsigned int nHashB = HashString(string_in, HASH_B);//要放入hashB表中的数据
unsigned int nHashStart = nHash % nTableSize;//计算起始查询位置
unsigned int nHashPos = nHashStart;//初始化实际查询到的位置
while (HashTable[nHashPos].bExists)//发生了碰撞
{
//校验碰撞原因
if (HashATable[nHashPos] == nHashA && HashBTable[nHashPos] == nHashB)
break; //原始数据发生了重复,可能的情况:1.发生了错误,2.我们执行的是查询
else
nHashPos = (nHashPos + 1) % nTableSize;//说明数据没有重复,但原来的位置上有数据,也就是发生了碰撞的情况,所以将实际位置偏移
if (nHashPos == nHashStart)
cout<<"字符串"<<string_in<<"不在表中"<<endl;
return 0;
}
if(strlen(data[nHashPos]))
{
cout<<"字符串"<<string_in<<"在表中"<<nHashPos<<endl;
return nHashPos;
}
else
cout<<"字符串"<<string_in<<"不在表中"<<endl;
}
/*生成密码表*/
void prepareCryptTable()
{
unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;
for( index1 = 0; index1 < 0x100; index1++ )
{
for( index2 = index1, i = 0; i < 5; i++, index2 += 0x100 )
{
unsigned long temp1, temp2;
seed = (seed * 125 + 3) % 0x2AAAAB;
temp1 = (seed & 0xFFFF) << 0x10;
seed = (seed * 125 + 3) % 0x2AAAAB;
temp2 = (seed & 0xFFFF);
cryptTable[index2] = ( temp1 | temp2 );
}
}
}
private:
/*按照dwHashType定义的类型取得字符串lpszFileName的各项hash值*/
unsigned long HashString(const char *lpszFileName, unsigned long dwHashType )
{
unsigned char *key = (unsigned char *)lpszFileName;
unsigned long seed1 = 0x7FED7FED;
unsigned long seed2 = 0xEEEEEEEE;
int ch;
while( *key != 0 )
{
ch = *key++;
seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
}
return seed1;
}
};
void main()
{
CHashForMpq cHashForMpq;
cHashForMpq.prepareCryptTable();
cHashForMpq.insert_string("abcdefghijklmnopqrstuvwxyz");
cHashForMpq.insert_string("abcd");
cHashForMpq.insert_string("ab");
cHashForMpq.insert_string("ab");
cHashForMpq.search_string("abcd");
cHashForMpq.search_string("efd");
getchar();
}