You are on page 1of 18

CHUYÊN ĐỀ THAM GIA TRẠI HÈ HÙNG VƢƠNG XII

MÔN TIN HỌC – MÃ CHẤM: Ti04a

HÀM BĂM VÀ ỨNG DỤNG


Phần thứ nhất
MỞ ĐẦU
Hàm băm (tiếng Anh: hash function) là giải thuật nhằm sinh ra các giá trị
băm tƣơng ứng với mỗi khối dữ liệu (có thể là một chuỗi kí tự, một đối tƣợng
trong lập trình hƣớng đối tƣợng, v.v...). Giá trị băm đóng vai gần nhƣ
một khóa để phân biệt các khối dữ liệu.Tuy nhiên, ngƣời ta chấp nhận hiện
tƣợng trùng khóa hay còn gọi là đụng độ và cố gắng cải thiện giải thuật để giảm
thiểu sự đụng độ đó.
Hàm băm thƣờng đƣợc dùng trong bảng băm nhằm giảm chi phí tính
toán khi tìm một khối dữ liệu trong một tập hợp (nhờ việc so sánh các giá trị
băm nhanh hơn việc so sánh những khối dữ liệu có kích thƣớc lớn).
Vì tính thông dụng của bảng băm, ngày nay đa số ngôn ngữ lập trình đều
cung cấp thƣ viện ứng dụng bảng băm, thƣờng gọi là thƣ viện collection trong
đó có các vấn đề nhƣ: tập hợp (collection), danh sách (list), bảng(table), ánh
xạ(mapping), từ điển (dictionary)). Thông thƣờng, các lập trình viên chỉ cần viết
hàm băm cho các đối tƣợng nhằm tích hợp với thƣ viện bảng băm đã đƣợc xây
dựng sẵn.
Một hàm băm tốt phải thỏa mãn các điều kiện sau:
 Tính toán nhanh.
 Các khoá đƣợc phân bố đều trong bảng.
 Ít xảy ra đụng độ.
 Xử lý đƣợc các loại khóa có kiểu dữ liệu khác nhau.
Việc hiểu và vận dụng thành thạo hàm băm trong các bài toán tin học đem
lại hiệu quả thiết thực cho học sinh đội tuyển HSGQG. Có nhiều bài toán sẽ đạt
kết quả cao hơn rất nhiều khi học sinh sử dụng hàm băm để giải nó.
Trong chuyên đề này, tác giả bƣớc đầu đề cập tới một vài khái niệm mang
tính khái quát về hàm băm. Đặc biệt tác giả sƣu tầm một sốbài toánliên quan đến
xử lí xâu kí tự và đã vận dụng thành công việc vận dụng hàm băm vào giải quyết
bài toán.
Rất mong đƣợc sự góp ý chân thành của các bạn!

2
Phần thứ hai
NỘI DUNG CHUYÊN ĐỀ
I. SƠ LƢỢC VỀ HÀM BĂM
I.1. Khái niệm
Các phép toán trên các cấu trúc dữ liệu nhƣ danh sách, cây nhị phân,…
phần lớn đƣợc thực hiện bằng cách so sánh các phần tử của cấu trúc, do vậy thời
gian truy xuất không nhanh và phụ thuộc vào kích thƣớc của cấu trúc.
Bảng băm giúp hạn chế số lần so sánh, do đó giảm thiểu đƣợc thời gian
truy xuất. Độ phức tạp của các phép toán trên bảng băm thƣờng có bậc là O(1)
và không phụ thuộc vào kích thƣớc của bảng băm.
Các phép toán chính thƣờng dùng trên cấu trúc bảng băm:
 Phép băm hay hàm băm (hash function)
 Tập khoá của các phần tử trên bảng băm
 Tập địa chỉ trên bảng băm
 Phép toán thêm phần tử vào bảng băm
 Phép toán xoá một phần tử trên bảng băm
 Phép toán tìm kiếm trên bảng băm
Thông thƣờng bảng băm đƣợc sử dụng khi cần giải quyết những bài toán
có các cấu trúc dữ liệu lớn và đƣợc lƣu trữ ở bộ nhớ ngoài.
Phép băm (Hash Function): Trong hầu hết các ứng dụng, khoá đƣợc dùng
nhƣ một phƣơng thức để truy xuất dữ liệu một cách gián tiếp. Hàm đƣợc dùng
để ánh xạ một khoá vào một dãy các số nguyên và dùng các giá trị nguyên này
để truy xuất dữ liệu đƣợc gọi là hàm băm.
Vấn đề: Cho trƣớc một tập S gồm các phần tử đƣợc đặc trƣng bởi giá trị
khóa. Trên giá trị các khóa này có quan hệ thứ tự. Tổ chức S nhƣ thế nào để tìm
kiếm 1 phần tử có khóa k cho trƣớc có độ phức tạp ít nhất trong giới hạn bộ nhớ
cho phép?
Ý tưởng: Biến đổi khóa k thành một số (bằng hàm hash) và sử dụng số
này nhƣ là địa chỉ để tìm kiếm trên bảng dữ liệu.

3
Hình vẽ dƣới đây minh họa cho ý tƣởng này:

Nhƣ vậy, hàm băm là hàm biến đổi khóa của phần tử thành địa chỉ trên
bảng băm.
Khóa có thể là dạng số hay số dạng chuỗi. Giải quyết vấn đề băm với các
khoá không phải là số nguyên:
 Tìm cách biến đổi khoá thành số nguyên
Ví dụ loại bỏ dấu „-‟ trong mã số 9635-8904 đƣa về số nguyên 96358904
Đối với chuỗi, sử dụng giá trị các ký tự trong bảng mã ASCCI
 Sau đó sử dụng các hàm băm chuẩn trên số nguyên.
I.2. Hàm Băm sử dụng Phƣơng pháp chia
 Dùng số dƣ:
o h(k) = k mod m
o k là khoá, m là kích thƣớc của bảng.
 Vấn đề chọn giá trị m
o m = 2n (không tốt)
o nếu chọn m= 2n thông thƣờng không tốt h(k) = k mod 2n sẽ chọn
cùng n bits cuối của k
o m là nguyên tố (tốt). Thông thƣờng m đƣợc chọn là số nguyên tố
n
gần với 2 . Chẳng hạn bảng ~4000 mục, chọn m = 4093
I.3. Hàm Băm sử dụng Phƣơng pháp nhân
 Sử dụng
o h(k) = m (k A mod 1)
o k là khóa, m là kích thƣớc bảng, A là hằng số: 0 < A < 1
 Chọn m và A
o m thƣờng chọn m = 2p

4
o Sự tối ƣu trong việc chọn A phụ thuộc vào đặc trƣng của dữ liệu.
o Theo Knuth chọn A = 1/2( 5 -1)  0.618033987 đƣợc xem là tốt.
I.4. Phép băm phổ quát
 Việc chọn hàm băm m không tốt có thể dẫn đến xác suất đụng độ lớn.
 Giải pháp:
o Lựa chọn hàm băm h ngẫu nhiên.
o Chọn hàm băm độc lập với khóa.
o Khởi tạo một tập các hàm băm H phổ quát và từ đó h đƣợc chọn
ngẫu nhiên.
o Một tập các hàm băm H là phổ quát (universal ) nếu với
mọi  f,k  H và 2 khoá k, l ta có xác suất: Pr{f(k) = f(l)} <= 1/m
Ví dụ: Giả sử nếu khoá là một số nguyên, dƣơng và HK(key) là một số
nguyên với một digit từ 0..9, Thế thì, hàm băm sẽ dùng toán tử modulo-10 để trả
về giá trị tƣơng ứng của một khoá. Chẳng hạn: nếu khoá=49 thì HF(49)=9.
Một cách tổng quát, với một hàm băm, nhiều khoá khác nhau có thể cho
cùng một giá trị băm. Trong tình huống này xảy ra sự xung đột (collision) và cần
thiết phải giải quyết sự đụng độ này. Một trong những phƣơng pháp giải quyết
sự xung đột với thời gian nhanh là sử dụng các cấu trúc danh sách đặc, hay danh
sách kề có kích thƣớc cố định.
Các cấu trúc bảng băm đơn giản, thƣờng đƣợc cài đặt bằng các danh sách
kề. Do vậy, để truy xuất một phần tử trên các bảng băm thuộc loại này, chỉ cần
hai khóa tƣơng ứng với hàng thứ i và cột thứ j để định vị một phần tử trên bảng.
Mở rộng: Có nhiều bảng băm khác nhƣ: Bảng băm chữ nhật (m hàng, n
cột),Bảng băm tam giác dƣới (m hàng) và bảng băm tam giác trên (n cột), Bảng
băm đƣờng chéo (n cột), Bảng băm ADT,…
Với mỗi loại bảng băm cần thiết phải xác định tập khóa K, xác định tập địa
chỉ M và xây dựng hàm băm HF (Hash Function) cho phù hợp.
Mặt khác, khi xây dựng hàm băm cũng cần thiết phải tìm kiếm các giải
pháp để giải quyết sự xung đột, nghĩa là giảm thiểu sự ánh xạ của nhiều khoá
khác nhau vào cùng một địa chỉ (ánh xạ nhiều-một).

5
II. MỘT SỐ BÀI TẬP VẬN DỤNG

Bài 1: Xâu con (http://vn.spoj.com/problems/SUBSTR/)


Cho xâu A và xâu B chỉ gồm các chữ cái thƣờng. Xâu B đƣợc gọi là xuất
hiện tại vị trí i của xâu A nếu: A[i] = B[1], A[i+1] = B[2], ..., A[i+length(B)-1] =
B[length(B)].
Yêu cầu: Hãy tìm tất cả các vị trí mà B xuất hiện trong A.
Input
 Dòng 1: xâu A.
 Dòng 2: xâu B.
Độ dài A, B không quá 1000000.
Output
Ghi ra các vị trí tìm đƣợc trên 1 dòng (thứ tự tăng dần). Nếu B không xuất hiện
trong A thì bỏ trắng.
Example

Input:
aaaaa
aa
Output:
1234
Thuật toán:
Mảng a[],b[] chứa các kí tự xâu a,b.
Mảng A[] là mã hash của xâu a[1..i] . Nghĩa là xâu tạo thành từ các kí tự từ
1..i của mảng a biểu diễn mã là A[i];
Tƣơng tự với mảng B[] là mã hash của xâu b[1..i] . Nghĩa là xâu tạo thành
từ các kí tự từ 1..i của mảng b biểu diễn mã là B[i];
Ta xây dựng 1 hàm get(long long h[],int l,int r) để lấy mã hash của mảng h
trong đoạn từ l đến r trong xâu;
Giờ ta chỉ phải kiểm tra từng mã hash độ dài n của xâu A có bằng mã hash
độ dài n của xâu B không.

6
Code Pascal:
const
fi='';
fo='';
maxn=trunc(1e6);
base=trunc(1e9)+7;
pp=307;
var
f : array[0..maxn] of int64;
i,j,n,m : longint;
hb : int64;
a,b : array[1..maxn] of byte;
ha,pow : array[0..maxn] of int64;
procedure enter;
var x : ansistring;
begin
assign(input,fi);reset(input);
readln(x);
m := length(x);
for i := 1 to m do a[i] := ord(x[i])-48;
readln(x);
n := length(x);
for i := 1 to n do b[i] := ord(x[i])-48;
close(input);
end;
function gethash(l,r : longint) : int64;
begin
gethash := (ha[r]-ha[l-1]*pow[r-l+1] + base*base) mod base;
end;
procedure process;
begin
assign(output,fo);rewrite(output);
ha[0] := 0;
for i:=1 to m do ha[i] := (ha[i-1]*pp + a[i]) mod base;
pow[0] := 1;
for i:=1 to m do pow[i] := pow[i-1]*pp mod base;
hb := 0;
for i:=1 to n do hb := (hb*pp + b[i]) mod base;
for i:=1 to m-n+1 do
if hb = gethash(i,i+n-1) then
write(i,' ');
close(output);
end;
begin
enter;

7
process;
end.
Codes Blocks :
#include <bits/stdc++.h>
#define FORE(i, a, b) for(int i = a; i <= b; i++)
#define FORD(i, a, b) for(int i = a; i >= b; i--)
#define FOR(i, a, b) for(int i = a; i < b; i++)
const int MAXN = 1e6 +10;
const int INF = 1e9 + 7;
using namespace std;
//string a,b;
int m,n;
char a[MAXN],b[MAXN];
long long A[MAXN],B[MAXN],M[MAXN];
int get(long long h[],int l,int r)
{
return (h[r] - h[l-1]*M[r-l+1] + 1LL*INF*INF)%INF;
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("substr.inp", "r", stdin);
freopen("substr.out", "w", stdout);
#endif
M[0]=1; M[1]=2309;
FOR(i,2,MAXN) M[i]=M[i-1]*M[1] %INF;
cin>>a+1; m=strlen(a+1);
cin>>b+1; n=strlen(b+1);
//FORE(i,1,m) cout<<a[i]<<endl;
FORE(i,1,m) A[i]=(A[i-1]*M[1]+a[i])%INF;
FORE(i,1,n) B[i]=(B[i-1]*M[1]+b[i])%INF;
//FORE(i,1,m) cout<<A[i]<<endl;
FORE(i,1,m-n+1)
{
if (get(A,i,i+n-1)==B[n]) cout<<i<<' ';
}
return 0;
}

8
Bài 2: Tiền tố và hậu tố http://vn.spoj.com/problems/C11STR2/)
Xâu a đƣợc gọi là TIềN Tố của xâu b nếu xâu a trùng với phần đầu của xâu
b. Ví dụ pre là tiền tố của prefix
Xâu a đƣợc gọi là HậU Tố của xâu b nếu xâu a trùng với phần cuối của xâu
b. Ví dụ fix là hậu tố của suffix
YENTHANH132 vừa mới học về tiền tố và hậu tố nên hôm nay anh ta sẽ
đố các bạn một bài toán đơn giản về tiền tố và hậu tố nhƣ sau:
 Cho 2 xâu a,b gồm các kí tự latin thƣờng ('a' đến 'z')
 Tìm 1 xâu c thỏa mãng:
1. Xâu a là tiền tố của xâu c
2. Xâu b là hậu tố của xâu c
3. Độ xài xâu c là ngắn nhất.
Input
 Dòng 1: Xâu a
 Dòng 2: Xâu b
Output
 Một dòng duy nhất là xâu c.
Giới hạn:
 40% số test có độ dài 2 xâu a,b <= 1000 kí tự
 Trong toàn bộ test, độ dài 2 xâu a,b <= 105 kí tự
 2 xâu a,b không nhất thiết phải khác nhau
Ví dụ:

Input 1 Output 1 Input 2 Output 2

abca abcab abc abc


cab abc

Thuật toán:Để thỏa mãn xâu c có độ dài ngắn nhất thì phần hậu tố xâu a
phải trùng càng nhiều với phần tiền tố xâu b.
Chúng ta kiểm tra việc hậu tố xâu a có giống tiền tố xâu b không bằng cách
xử dụng hash để giảm độ phức tạp thuật toán.

9
Gọi c = a + b;
Ta sẽ duyệt số ký tự cần xóa để xâu c thỏa mãn điều kiện đề bài.
Theo bài ra điều kiện để xâu c chấp nhận b[1,i] = a[m-i+1,m].
Để thuật toán so sánh xâu giảm độ phức tạp, ta sử dụng Hash.
Code Pascal:
{$H+}
uses math;
const
fi='';
fo='';
base=trunc(1e9);
pp=307;
maxn=trunc(1e5);
var
a,b,c : string;
i,j,n,m : longint;
ha,hb : array[0..maxn] of int64;
pow : array[0..maxn] of int64;
procedure enter;
begin
assign(input,fi);reset(input);
readln(a);
readln(b);
close(input);
end;
procedure swap( var m,n : longint; var a,b : string);
var tg1 : longint;
tg2 : string;
begin
tg1 := m ; m := n ; n:= tg1;
tg2 := a; a :=b ; b:= tg2;
end;
function getha(l,r : longint) : int64;
begin
getha := (ha[r]-ha[l-1]*pow[r-l+1] + base*base) mod base;
end;
function gethb(l,r : longint) : int64;
begin
gethb := (hb[r]-hb[l-1]*pow[r-l+1] + base*base) mod base;
end;

10
procedure process;
begin
m := length(a); n := length(b);
c := a + b;
//if m<n then swap(m,n,a,b);
pow[0] := 1;
for i:=1 to max(m,n) do pow[i] := pow[i-1]*pp mod base;
ha[0] := 0; hb[0] := 0;
for i:=1 to m do ha[i] := (ha[i-1]*pp + ord(a[i])-48) mod base;
for i:=1 to n do hb[i] := (hb[i-1]*pp + ord(b[i])-48) mod base;
for i:=min(m,n) downto 1 do
if gethb(1,i) = getha(m-i+1,m) then
begin
delete(c,m-i+1,i);
break;
end;
end;
procedure print;
begin
assign(output,fo);rewrite(output);
writeln(c);
close(output);
end;
begin
enter;
process;
print;
end.
Codes Blocks:
#include <bits/stdc++.h>
#define FORE(i, a, b) for(int i = a; i <= b; ++i)
#define FORD(i, a, b) for(int i = a; i >= b; --i)
#define FOR(i, a, b) for(int i = a; i < b; ++i)
#define X first
#define Y second
#define long long long
const int MAXN = 1e5 +10;
const long INF = 1e9 + 9;
const int N = 5000;

using namespace std;


int n,m;
string a,b;
long A[MAXN],B[MAXN],M[MAXN];
long get(int l,int r)

11
{
return (A[r]-A[l-1]*M[r-l+1]+INF*INF)%INF;
}
int main()
{
ios::sync_with_stdio(0); cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("c11str2.inp", "r", stdin);
freopen("c11str2.out", "w", stdout);
#endif
cin>>a; n=a.size();
cin>>b; m=b.size();
M[1]=2802;
FORE(i,2,MAXN-5) M[i]=M[i-1]*M[1]%INF;
FORE(i,1,n) A[i]=(A[i-1]*M[1]+a[i-1])%INF;
FORE(i,1,m) B[i]=(B[i-1]*M[1]+b[i-1])%INF;
int ll=min(m,n),k=-1;
FORD(i,ll,1)
if (get(n-i+1,n)==B[i])
{
k=i-1;
break;
}
cout<<a;
FORE(i,k+1,m-1) cout<<b[i];
return 0;
}
Bài 3: ESIGN (Bài của thầy Nguyễn Thanh Tùng)
Trong chữ ký điện tử có phần nhận dạng ngƣời gửi là dãy số nguyên P = (p1, p2,
. . ., pn). P đƣợc gọi là Mã nhận dạng tên. Ở công ty Aurora mã nhận dạng tên
của mỗi ngƣời đƣợc xây dựng theo quy tắc sau:
Trích một dãy con độ dài n các phần tử liên tiếp nhau của dãy số nguyên
A = (a1, a2, . . ., am),
Hoán vị một cách ngẫu nhiên các vị trí trong dãy con trích đƣợc.
Ví dụ, với m= 6 và A = (2, 1, 4, 6, 3, 9) và n = 4, mã nhận dạng tên có thể là (1,
4, 6, 3), (3, 4, 6, 1), . . . nhƣng (1, 2, 4, 9), (2, 5, 6, 4) không phải là mã nhận
dạng tên của ngƣời trong Công ty.
Cấp bậc của nhân viên trong Công ty càng cao thì vị trí đầu của dãy con đƣợc
trích ra trong A càng nhỏ. Ở ví dụ trên nhân viên với mã nhận dạng (3, 4, 6, 1)
có cấp bậc 2.

12
Cho P và A của một văn bản điện tử. Hãy xác định xem tác giả của văn bản có
phải là ngƣời của Công ty hay không và đƣa ra thông báo tƣơng ứng “YES”
hoặc “NO”. Nếu là ngƣời của Công ty, hãy chỉ ra cấp bậc của tác giả.
Dữ liệu: Vào từ file văn bản ESIGN.INP:
Dòng đầu tiên chứa một số nguyên n (1 ≤ n ≤ 105),
Dòng thứ 2 chứa n số nguyên p1, p2, . . ., pn (1 ≤ pi ≤ 105, i = 1 ÷ n),
Dòng thứ 3 chứa số nguyên m (n ≤ m ≤ 105),
Dòng thứ 2 chứa m số nguyên a1, a2, . . ., am (1 ≤ aj ≤ 105, j = 1 ÷ m).
Kết quả: Đƣa ra file văn bản ESIGN.OUT thông báo “YES” hoặc “NO”. Nếu là
ngƣời của Công ty thì ở dòng tiếp theo đƣa ra một số nguyên – cấp bậc của
ngƣời đó.
Ví dụ:
ESIGN.INP ESIGN.OUT
3 YES
234 2
4
1423
Thuật toán: Bài toán trên đƣa về bài toán kiểm tra 2 xâu có là hoán vị của
nhau.Để kiểm tra nhanh ta vẫn áp dụng tƣ tƣởng thuật toán Hash. Nhƣng khác ở
chỗ tính mảng băm bằng công thức: h[i] := h[i-1] + pow[a[i]];
Code Pascal:
const fi='esign.inp';
fo='esign.out';
maxn=trunc(1e5);
hb=307;
pp=trunc(1e9)+7;
var h :array[0..maxn] of int64;
i,j,n,m :longint;
a :array[0..maxn] of longint;
hp,hq,ha :int64;
ans :longint;
procedure enter;
var t :longint;
begin
h[0]:=1;
for i:=1 to maxn do h[i]:=(h[i-1]*hb) mod pp;
read(n);
for i:=1 to n do

13
begin
read(t);
hp := (hp+h[t]) mod pp;
end;
end;
procedure process;
begin
readln(m);
for i:=1 to m do
begin
read(a[i]);
end;
ha := 1;
for i:=1 to n-1 do ha := (ha+h[a[i]]) mod pp;
a[0] :=0;
for i:=1 to m-n+1 do
begin
ha := (ha+h[a[i+n-1]]-h[a[i-1]]) mod pp;
if ha=hp then
begin
ans := i;
break;
end;
end;
end;
procedure print;
begin
if ans=0 then
begin
writeln('NO');
end
else
begin
writeln('YES');
writeln(ans);
end;
end;
begin
assign(input,fi);reset(input);
assign(output,fo);rewrite(output);
enter;
process;
print;
close(input);close(output);
end.

14
Bài 4: Writing (http://vn.spoj.com/problems/PBCWRI/)
Cho 2 chuỗi A,B chứa các chữ cái trong bảng chữ tiếng Anh (có cả chữ hoa
và chữ thƣờng). Chuỗi A có độ dài n, chuỗi B có độ dài m.
Yêu cầu: Đếm số lần xuất hiện của các hoán vị của chuỗi A trong chuỗi B.
Dữ liệu:
 Dòng đầu tiên chứa 2 số nguyên n và m.
 Dòng thứ 2 chứa n kí tự của chuỗi A.
 Dòng thứ 3 chứa m kí tự của chuỗi B.
Kết qủa:
 Một số duy nhất là kết quả của bài toán.
Giới hạn: n ≤ 3000; m ≤ 3 000 000;
Ví dụ:

Dữ liệu Kết quả


4 11 2

cAda
AbrAcadAbRa

Giải thích: 2 lần bắt đầu từ vị trí 4 và 5.


Thuật toán:Sử dụng thuật toán so sánh nhanh 2 xâu có là hoán vị của nhau
không nhƣ bài 3.
Code Pascal:
{$H+}
const
fi='';//pbcwri.inp';
fo='';//pbcwri.out';
maxn=3000;
maxm=3000000;
pp=307;
base=trunc(1e9)+7;
var
ha,tg : int64;
hb : array[0..maxm] of int64;
pow : array[0..100] of int64;

15
i,j,n,m,dem : longint;
aa : array[0..maxn] of longint;
bb : array[0..maxm] of longint;
a,b : string;
begin
assign(input,fi);reset(input);
assign(output,fo);rewrite(output);
readln(n,m);
readln(a);
readln(b);
for i:=1 to n do aa[i] := ord(a[i])-ord('A');
for i:=1 to m do bb[i] := ord(b[i])-ord('A');
pow[0] := 1;
for i:=1 to 100 do pow[i] := pow[i-1]*pp mod base;
for i:=1 to n do ha := (ha + pow[aa[i]]) mod base;
for i:=1 to m do hb[i] := (hb[i-1] + pow[bb[i]]) mod base;
//tg := hb[n-2];
//for i:=1 to n-1 do tg := tg + pow[bb[i]]; tg := tg +1;
for i:=1 to m-n+1 do
begin
tg := (hb[i+n-1] - hb[i-1] + base*base) mod base;
if tg = ha then inc(dem);
end;
writeln(dem);
close(input);close(output);
end.

16
Phần thứ ba
KẾT LUẬN
Các bài toán vận dụng hàm băm vào giải quyết thật tuyệt vời. Chƣơng trình
chạy nhanh hơn nên luôn đáp ứng đƣợc về mặt thời gian cho phép. Để có thể
nghiên cứu kĩ và sâu hơn về hàm băm, tác giả xin dành để một dịp khác nhé.
Cảm ơn các bạn đã đọc tài liệu!

Tác giả
Nguyễn Thị Hợp

17
TÀI LIỆU THAM KHẢO
1. Trần Đỗ Hùng, Chuyên đề bồi dưỡng HSG Tin học THPT,NXB GD 2007
2. Tài liệu sách giáo khoa chuyên tin
3. Nguyễn Xuân My, Một số vấnđề chọn lọc trong môn Tin học, NXB GD
2002
4. Trang web: http://www.vn.spoj.com

18

You might also like