题目链接 http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_4_C
输入:数量n 下面n行输入命令,insert "字符串"和find"字符串"
输出:对于insert命令建立一个散列表;对于find命令输出yes或者no,来标识表中是否有该字符串。
思路:所谓散列表,希望能对给定的输入key值,对应于表中的一个位置,输出这个key在散列表中存储的位置。有key到位置的固定的计算方法(函数),由人为规定。好处在于,位置直接和key值关联,只要知道key值可以瞬间定位到在表中存储的位置,插入和查找的复杂度都是O(1);
建立散列表的方法有两种,opening hash和closed hash,第一种是在数组后面挂链表的做法,我采取的是后面一种,开放地址法 open addressing
对于输入的一个字符串,本题只输入 A C G T 这四个字符组成的字符串,先把他们转换成对应四个数字,然后转换成对应的key值,我采取的是A-1 C-2 G-3 T-4 然后自己规定一个算法求出key值(当然,字符串对应的key值是惟一的,但是key值不能对应回唯一的字符串),我使用的方法如下
for(ll i=0;i<len;i++){//len是字符串长度
sum+=p*chtonum(s[i]);//chtonum是char to num字符串对应到数字,p初始为1,sum即是这个字符串对应的key值
p*=5;
}
对应到这个字符串的key值之后(好像也称之为字符串的hash值,典型的算法比如MD5,SHA1之类的,这样能基本上使不同的字符串对应不同的hash值,我采用的是最简单的算法,即很有可能不同字符串对应相同的key值),然后我希望能得到key在散列表中对应存储的位置h,我采用的函数是:h=(h1(key)+i*h2(key))%m;
其中
h1=key%m
h2=1+(key&(m-1))
这里的m常取的是散列表的范围,因为一个数模m,所得到的一定是一个在[0,m)之间的数,所以h得到的肯定是一个在散列表范围内的数,但是可能会存在不同字符串产生相同key值的情况,(即这个字符串的hash值对应的位置已经被别的元素占了)所以引入了i这个变量:随着i的取值变化,h会对应不同的位置;一般把i从0递增来取,这样会使得h的取值遍历整个散列表,直到找到一个可以存放的位置,或者无处安放。一般为了能让h的取值随着i的变化包含所有的散列表的范围,我们一般取一个与h2(k)互质的m,所有一般取一个比h2(k)大的质数。为了防止无法生成位置。试想,如果h2(k)能被m整除,则i的取值都没有任何意义了。
在查找的时候,只需要重复上述函数过程即可。
代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
#define row 1001333 //取一个比h2(k)大的质数
#define col 20
char H[row][col];
ll h1(ll key){
return key%row;
}
ll h2(ll key){
return 1+(key%(row-1));
}
ll chtonum(char s){ //character to number
if(s=='A') return 1;
if(s=='C') return 2;
if(s=='G') return 3;
if(s=='T') return 4;
return 0;
}
ll get_key(char s[]){ //生成key值
ll sum=0,p=1;
int len=strlen(s);
for(ll i=0;i<len;i++){
sum+=p*chtonum(s[i]);
p*=5;
}
return sum;
}
void insert(char s[]){
ll key,h;
key=get_key(s);
ll i =0;
while(1){
h=(h1(key)+i*h2(key))%row; //根据key值生成对应的位置
if(strcmp(H[h],s)==0) return ;//这个元素字典里已经有了
else if (strlen(H[h])==0){
strcpy(H[h], s);//如果该位置没有别的值,就把这个元素插进去
return ;
}
i++;//如果这个i对应的h的位置已经有元素了,那么继续寻找下一个i对应的位置
}
}
bool find(char s[]){
ll key,h;
key=get_key(s);
ll i=0;
while(1){
h=(h1(key)+i*h2(key))%row;
if(strcmp(H[h],s)==0) return true;//如果找到了该元素
else if (strlen(H[h])==0) return false;
i++;
}
return false;
}
int main (){
int n;
char c[20],s[20];
for(int i = 0;i<row;i++) H[i][0]='\0'; //把数组初始化 方便比较
scanf("%d",&n);
for(int i =0;i<n;i++){
cin>>c>>s;
if(c[0]=='i') insert(s);
else {
if(find(s)) cout<<"yes"<<endl;
else cout<<"no"<<endl;
}
}
return 0;
}
错点:
1.find函数中一定要有 else if (strlen(H[h])==0) return false; 否则无法出循环
2.数据范围是long long
3.散列表第一个元素要初始化,否则不方便比较是否为空
4字符串读入过程中遇到玄学错误暂时没有找到错误,排错一个下午没找出错误,跑了无数次始终无法读入字符串,结果晚上11点多看完rng小组第一之后,啥也没改莫名其妙就正常了:D,恭喜RNG