散列表(一)基础

很久以前就接触过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.

posted on 2011-06-22 23:27  oa414  阅读(242)  评论(0编辑  收藏  举报

导航