散列表(一)基础

很久以前就接触过hash,可是当时链表都没有写过,毕竟在竞赛中用得太少了。近段时间做了不少网络流的题目,都是数组模拟的链表存储图的,再回过头看hash,就很好理解了。

hash的资料到处都有,就不再废话了。处理冲突一般有两种:分离链接法和开放定址法。就是挂链表和抢别人的位置嘛。至于什么平方取中啊,再散列啊,虽然有所了解,但还是继续纸上谈兵吧。实际应用的话熟练掌握几个hash函数的方法并能熟练运用就行了。

相关资料有:

 字符串Hash函数对比

各种字符串Hash函数比较

关于hash的误区与我的看法

http://www.nocow.cn/index.php/BKDRHash

 

最常用的就是把一个字符串函数hash然后挂链表了,附POJ2503程序:

const

  maxsize=99997;

var

  s,enword,foreign:string;

  en,str:array[0..maxsize*10] of string;

  head:array[0..maxsize] of longint;

  next:array[0..maxsize*10] of longint;

  size:longint;

  space:longint;



function hash(var s:string):longint;

const

  seed=31;

var

  i:longint;

begin

  hash:=0;

  for i:=1 to length(s) do

    hash:=(hash*seed+ord(s[i])) and $FFFFFF;

  exit(hash mod maxsize);

end;





procedure insert(var enword,foreign:string);

var

  hashval:longint;

begin

  inc(size);

  str[size]:=foreign;

  en[size]:=enword;

  hashval:=hash(foreign);

  next[size]:=head[hashval];

  head[hashval]:=size;

end;



function find(var s:string):string;

var

  i:longint;

begin

  i:=head[hash(s)];

  while i<>0 do

  begin

    if str[i]=s then exit(en[i]);

    i:=next[i];

  end;

  exit('eh');

end;





begin

  readln(s);

  while s<>'' do

  begin

    space:=pos(' ',s);

    enword:=copy(s,1,space-1);

    foreign:=copy(s,space+1,length(s)-space);

    insert(enword,foreign);

    readln(s);

  end;

  while not eof do

  begin

    readln(s);

    writeln(find(s));

  end;

end.



有时候用tire也不错,附单词查找树的代码,虽然与hash无直接关系。

 

var

  tree:array[0..500000,'A'..'Z']of longint;

  tot,i,j,k:longint;

  s:ansistring;





procedure insert(s:ansistring);

var

  i,j,k,x:longint;

begin

  x:=1;

  for i:=1 to length(s) do

  begin

    if tree[x,s[i]]=0 then

    begin

      inc(tot);

      tree[x,s[i]]:=tot;

      x:=tot;

    end else

    x:=tree[x,s[i]];

  end;

end;



begin

  assign(input,'tree.in');

  reset(input);

  assign(output,'tree.out');

  rewrite(output);



  fillchar(tree,sizeof(tree),0);

  tot:=0;

  while not eof do

  begin

    readln(s);

    insert(s);

  end;

  writeln(tot+1);



  close(output);

  close(input);

end.

 

有时候可以根据数据的特殊性质来给出更有针对性的hash方法,比如hash一个排列,最好用康托展开

如果是不怎么长的固定的字符组成的字符串,则可以映射为一个数字,如POJ1200,附代码

{

一个字符串由 NC 种不同的字符构成,问其中字串长度为 N 的不同字串有多少个。

当然可以直接枚举,但题目中谈到了 NC,可将 NC 种不同的字符转化为 NC 进制数,然后将字串转化为整数来判断。

或者将它们转化为 hash 来判断。



http://www.chenyajun.com/2010/04/04/4774



http://www.cublog.cn/u3/102624/showart_2042806.html



练习hash的入门题,不过这题能充分利用条件产生nc进制的数来构造没有冲突hash函数



开始时把j打成i了……



2011年6月21日23:01:48

}



var

  s:ansistring;

  i,j,k,ans,n,m:longint;

  f:array['!'..'~'] of longint;

  sum:longint;

  flag:array[0..16000000] of boolean;

begin

  readln(n,m);

  readln(s);

  for i:=1 to length(s) do

  begin

    if f[s[i]]=0 then

    begin

      f[s[i]]:=k;

      inc(k);

    end;

    if k>m then break;

  end;

  ans:=length(s)-n+1;

  for i:=1 to length(s)-n+1 do

  begin

    sum:=0;

    for j:=i to i+n-1 do

      sum:=sum*m+f[s[j]];

    if not flag[sum] then

      flag[sum]:=true

    else dec(ans);

  end;

  writeln(ans);

end.



你可能感兴趣的:(基础)