Tải bản đầy đủ (.doc) (15 trang)

ỨNG DỤNG MẢNG BĂM (HASH) ĐỂ GIẢI CÁC BÀI TOÁN VỀ SO KHỚP XÂU (CHUỖI)

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (164.83 KB, 15 trang )

SỞ GIÁO DỤC VÀ ĐÀO TẠO NAM ĐỊNH
TRƯỜNG THPT CHUYÊN LÊ HỒNG PHONG
SÁNG KIẾN DỰ THI CẤP TỈNH
BÁO CÁO SÁNG KIẾN
ỨNG DỤNG MẢNG BĂM (HASH) ĐỂ GIẢI CÁC BÀI TOÁN VỀ SO KHỚP XÂU (CHUỖI)

Tác giả: Ngô Trung Tưởng
Trình độ chuyên môn: Đại học
Chức vụ: Giáo viên
Nơi công tác: Trường THPT chuyên Lê Hồng Phong

Nam Định, tháng 05 năm 2014
THÔNG TIN CHUNG VỀ SÁNG KIẾN
1. Tên sáng kiến:
ỨNG DỤNG MẢNG BĂM (HASH) ĐỂ GIẢI CÁC BÀI TOÁN VỀ SO KHỚP XÂU (CHUỖI)
2. Lĩnh vực áp dụng sáng kiến:
Giảng dạy lớp 10, 11 chuyên tin, đội tuyển HSGQG môn tin học
3. Thời gian áp dụng sáng kiến:
Từ năm học 2012-2013 đến nay
4. Tác giả:
Họ và tên: Ngô Trung Tưởng
Năm sinh: 1982
Nơi thường trú: 22/34 Trần Thái Tông, P Thống Nhất, TP Nam Định
Trình độ chuyên môn: Đại học
Chức vụ công tác: Giáo viên
Nơi làm việc: Trường THPT chuyên Lê Hồng Phong
Địa chỉ liên hệ: 22/34 Trần Thái Tông, P Thống Nhất, TP Nam Định
Điện thoại: 0982.209.259
5. Đơn vị áp dụng sáng kiến:
Tên đơn vị: Trường THPT chuyên Lê Hồng Phong
Địa chỉ: 76 Vỵ Xuyên


Điện thoại: 03503.640297


III. Nội dung chuyên đề
1. Phát biểu bài toán
- Cho một xâu T gồm m kí tự
- Cho một xâu mẫu P gồm n kí tự
- Yêu cầu: cho biết xâu mẫu P xuất hiện bao nhiêu lần trong T và chỉ ra các vị trí xuất hiện của P.
2. Thuật toán
Có rất nhiều thuật toán khác nhau để giải bài toán này như Brute-force approach (vét cạn-độ
phức tạp O(n*m)), KMP (độ phức tạp O(n+m)), Suffix Array…
Trong chuyên đề này tôi không đề cập đến các thuật toán trên mà chỉ tập trung vào một thuật
toán sử dụng mảng băm hay còn gọi một tên gọi khác là HASH. Đây là một thuật toán rất hiệu
quả đặc biệt là trong thi cử vì tốc độ thực thi nhanh, linh hoạt trong xử lí và rất dễ cài đặt.
Mô tả thuật toán HASH
a. Các kí hiệu:
- Tập các chữ cái trong bảng chữ cái kí hiệu ∑
- Xâu T gồm m kí tự: T[1..m]
- Xâu mẫu P gồm n kí tự: P[1..n]
- Xâu con từ i đến j của xâu T là: T[i..j]
- Chúng ta cần tìm tất cả các vị trí i thỏa mãn (1≤i≤m-n+1) mà T[i..i+n-1]=P[1..n]
b. Mô tả thuật toán
Để đơn giản, giả sử rằng Σ = {a, b,…, z}, nghĩa là Σ chỉ gồm các chữ cái Latin in thường. Để
biểu diễn một xâu, thay vì dùng chữ cái, chúng ta sẽ chuyển sang biểu diễn số ở hệ 26.
Ví dụ: xâu ‘abcd’ biểu diễn hệ 26: ‘a’*26 3+ ‘b’*262+ ‘c’*261 + ‘d’*260 đổi ra số ở hệ cơ số 10 tương
ứng là: 65*263+66*262+67*26+68= 1188866.
Dễ thấy rằng, muốn so sánh 2 xâu bằng nhau khi và chỉ khi biểu diễn của 2 xâu ở hệ cơ số 10
giống nhau.
Ví dụ xâu A=B ↔ Mã A = Mã B
Tuy nhiên nếu xâu quá dài thì Mã A, Mã B cũng rất lớn. Chính vì thế, ta lấy mod cho 1 số base

nguyên tố rất lớn nào đó ví dụ 109+7, hay 2.109+11…
A=B  Mã A mod base = Mã B mod base
Dễ dàng nhận thấy việc so sánh Mã A mod base với Mã B mod base rồi kết luận A có bằng với B
hay không là sai. Mã A mod base = Mã B mod base chỉ là điều kiện cần để A bằng B chứ chưa
phải điều kiện đủ. Tuy nhiên, chúng ta sẽ chấp nhận lập luận sai này trong thuật toán Hash. Và
coi điều kiện cần như điều kiện đủ. Trên thực tế, lập luận sai này có những lúc dẫn đến so sánh
xâu không chính xác và chương trình bị chạy ra kết quả sai. Nhưng cũng thực tế cho thấy rằng,
khi chọn base là một số nguyên lớn, số lượng những trường hợp sai rất ít, và ta có thể coi Hash
là một thuật toán chính xác.
Trở lại bài toán ban đầu, chúng ta cần chỉ ra P xuất hiện ở những vị trí nào trong T.
Để làm được việc này, chúng ta chỉ cần duyệt qua mọi vị trí xuất phát có thể của P trong T.
Giả sử vị trí đó là i, chúng ta sẽ kiểm tra T[i. . i + n − 1] có bằng với P hay không. Để kiểm tra điều
này, chúng ta cần tính được mã Hash của đoạn T[i. . i + n − 1] và mã Hash của xâu P.
Để tính mã Hash của xâu P chúng ta chỉ cần làm đơn giản như sau:
HashP=0;
for (i=1;i<=n;i++)
HashP = (HashP*26 + P[i]) % base;
Phần khó hơn của thuật toán Hash là: Tính mã Hash của một đoạn con từ i đến j T[i. . j] của xâu
T (1 ≤ i ≤ j ≤ m).
Ví dụ sau: Xét xâu s= “cdeacx” và biểu diễn của nó dưới cơ số 26. Chúng ta cần lấy mã Hash của
đoạn con từ phần tử thứ 3 đến phần tử thứ 6, chỉ cần lấy mã Hash của s[1. .6] trừ đi mã Hash
của s[1. .2] nhân với 264.
Để cài đặt ý tưởng này, chúng ta cần khởi tạo 26� mod base (0 ≤ i ≤ m) và mã Hash


của tất cả những tiền tố của s, cụ thể là mã Hash của những xâu s[1. . i] (1 ≤ i ≤ m).
//tinh 26x mod base
POW[0]=1;
for (i=1;i<=m;i++)
POW[i] = POW[i-1]*26 % base;

//tinh ma Hash xau s[1..i]
HashT[0]=0;
for(i=1;i<=m;i++)
HashT[i]=(HashT[i-1]*26+T[i])%base;
Để lấy mã Hash của T[i..j] ta viết hàm sau:
long long getHash(long i, long j){
return(HashT[j]-HashT[i-1]*POW[j-i+1]+ base*base)%base;
}
Bài toán chính được giải quyết như sau:
for (i=1;i<=m-n+1;i++)
if (getHash(i,i+n-1)==HashP)
cout<c. Viết chương trình
Đây là chương trình đầy đủ và đã được chấm thành công trên hệ thống chấm bài SPOJ
/>Chương trình viết bằng C++
#include <iostream>
#include <string>
#include <algorithm>
#include <fstream>
using namespace std;
const long long base=1000000000+7;
long long hasha[1000005], hashb,POW[1000005];
string a,b;
long n,m;
ifstream fi ("substr.inp");
ofstream fo ("substr.out");
long long gethash(long i, long j){
return(hasha[j]-hasha[i-1]*POW[j-i+1] +base*base)%base;
}
int main(){

//getline(cin,a,'\n');
getline(fi,a);
getline(fi,b);
n=a.size();
a=" "+a;
//getline(cin,b,'\n');
m=b.size();
b=" "+b;
//tinh hashb
hashb=0;
for (long i=1; i<=m; i++)
hashb= (hashb*26 + b[i])% base;
//tinh ham mu
POW[0]=1;


for (long i=1;i<=n;i++)
POW[i]=(POW[i-1]*26)% base;
//tinh hash xau a
hasha[0]=0;
for (long i=1; i<=n;i++)
hasha[i]=(hasha[i-1]*26+a[i]) % base;
//tim vi tri xau b trong a
for (long i=1; i<=n-m+1;i++)
if (hashb==gethash(i,i+m-1))
//cout<fo<//cin.get();
fi.close();
fo.close();

return 0;

}
d. Đánh giá:
Độ phức tạp của thuật toán là O(n + m). Nhưng điều quan trọng là: chúng ta có thể kiểm tra 2
xâu có giống nhau hay không trong O(1). Đây là điều tạo nên sự linh động cho thuật toán Hash.
4. Ứng dụng
Bài 1: Tiền tố và hậu tố
/>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ủaprefix
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ủasuffix
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ự
Ví dụ:
Input 1:
abca

cab
Output 1:
abcab
Input 2:


abc
abc
Output 2:
abc
(2 xâu a,b không nhất thiết phải khác nhau).
Hướng dẫn giải thuật:
- Tìm hậu tố dài nhất xâu a có phần chung tiền tố xâu b:
+ Với xâu a gọi ha[i] là mã hash của xâu a gồm các kí tự từ 1 đến i.
+ Với xâu b gọi hb[i] là mã hash cảu xâu b gồm các kí tự từ 1 đến i.
+ Tìm vị trí x mà tiền tố xâu b chung hậu tố xâu a ta chỉ so sánh mã hash của xâu con b[1],
…,b[x] với a[n-x+1],…,a[n] với n là độ dài xâu a. Duyệt x từ 1 đến độ dài xâu b tìm ra kết
quả.
- Độ phức tạp O(N)
Chương trình:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll BASE=1000000000+11;
string a,b;
ll ha[100005],hb[100005],POW[100005];
long n,m;
bool kt(long x){

ll xa,xb;
long n=a.size()-1;
xb=hb[x];
xa=(ha[n]-ha[n-x]*POW[x]+BASE*BASE)%BASE;
if (xa==xb) return 1;
else return 0;
}
int main(){
getline(cin,a);
a='@'+a;
getline(cin,b);
b='@'+b;
//tinh ham mu
POW[0]=1;
for (long i=1;iPOW[i]=POW[i-1]*26 % BASE;
//tinh hash xau a
ha[0]=0;
for (long i=1;iha[i]=(ha[i-1]*26+a[i])% BASE;
//tinh hash xau b
hb[0]=0;
for (long i=1;ihb[i]=(hb[i-1]*26+b[i])% BASE;
//tim hau to xau a la tien to xau b


long res=0;
for (long i=1;iif (kt(i)) res=i;

a.erase(0,1);//xoa ki tu @ them vao a
b.erase(0,1);//xoa ki tu @ them vao b
//cout<cout<//cin.get();
}


Bài 2: Sắp xếp Mã bài: VMSORT
Kì thi VM đang dần đi đến những vòng thi cuối cùng. Trong khi các thí sinh hăng say với những
bài tập hóc búa, mang đậm tính chất Marathon thì các admin VNOI cũng phải đối mặt với những
vấn đề rất nan giải, chẳng hạn như thống kê số lượng thí sinh, số lượng thí sinh giải được từng
bài tập...
Trong không khí căng thẳng của cuộc đua, ban tổ chức đã quyết định tặng một món quà đặc biệt
cho các bạn. Và món quà đó chính là bài tập này!
Bạn sẽ phải giúp các admin làm một nhiệm vụ sau: Cho dữ liệu mô tả các thí sinh tham gia mỗi
một trong K vòng của cuộc thi VNOI Marathone 20xx, hãy tính xem có bao nhiêu thí sinh tham
gia ít nhất một vòng thi.
Input
 Dòng 1: Ghi số nguyên dương K (1 ≤ K ≤ 5).
 K nhóm dòng sau: Mỗi nhóm thể hiện dữ liệu của một vòng thi: Dòng đầu ghi số nguyên
N (1 ≤ N ≤ 200) là số lượng thí sinh tham gia vòng thi đó. N dòng sau, mỗi dòng ghi một
nick của thí sinh, dưới dạng một xâu khác rỗng, độ dài không quá 20 kí tự (đảm bảo xâu
không chứa khoảng trống). Trong mỗi nhóm dòng mô tả một vòng thi, không có tên thí
sinh nào được lặp lại hai lần.
Output
 Ghi ra một số nguyên duy nhất là số thí sinh tham gia ít nhất một vòng thi.
Example
Input:
3

4
flashmt
ll931110
technolt
tuananhnb93
3
mr_invincible
conankudo
ll931110
3
khanhptnk
hphong
technolt
Output:
8
Hướng dẫn thuật toán:
- Mỗi xâu mã hóa dưới dạng số lưu vào một phần tử của mảng, sắp xếp lại mảng, đếm số
lượng số khác nhau trong mảng.
- Độ phức tạp O(K.N+N logN)
Chương trình C++
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
const long long BASE=1000000000+11;
long n,k,m=0;
long long hash;
long long a[100005];



string s;
int main(){
cin>>k;
for (int test=0;testcin>>n;
getline(cin,s);
for (long i=0;igetline(cin,s);
hash=0;
for (int j=0;jhash=(hash*256+s[j])% BASE;
a[m++]=hash;
}
}
sort(a,a+m);
long dem=1;
for (long i=1;iif (a[i]!=a[i-1]) dem++;
cout<cin.get();
return 0;
}


Bài 3 – Xâu con – COCI 2006-2007
Steve chiến thắng trong một lần cá cược và John phải mời anh ấy đi xem phim. Trong khi chờ đợi
Steve, John để ý một thông báo trên màn hình quảng cáo phía trên chỗ anh đang đứng.
Steve đến muộn nên John đã để ý trên màn hình quảng cáo một thời gian và thấy rằng có một
vài thông báo xuất hiện trên màn hình nhiều hơn một lần. Tự nhiên, anh ấy viết tất cả các thông
báo ra giấy. John muốn biết chiều dài của xâu dài nhất xuất hiện ít nhất hai lần (xuất hiện trong

hai vị trí khác nhau trên trang giấy)?
Yêu cầu: Hãy giúp John trả lời câu hỏi đó.
Dữ liệu vào cho trong tệp SUBSTR.INP
- Dòng 1 chứa số nguyên L (1≤L≤200000) là độ dài mà Jonh đã viết trên giấy.
- Dòng 2 chứa một xâu có độ dài L gồm các kí tự chữ thường trong bảng chữ cái tiếng anh.
Kết quả ghi ra tệp SUBSTR.OUT một số duy nhất là độ dài dài nhất của xâu

xuất hiện ít nhất hai lần trong xâu đã cho. Nếu không tồn tại xâu
con thỏa mãn đưa ra số 0.
SUBSTR.INP

SUBSTR.OUT

18
4
trutrutiktiktappop
Hướng dẫn:
- Nhận xét:
+ Nếu tồn tại một xâu con dài nhất độ dài l xuất hiện ít nhất 2 lần trong xâu s.
+ Tồn tại một xâu con có độ dài l-1 xuất hiện ít nhất 2 lần trong s và không tồn tại xâu con
có độ dài l+1 thỏa mãn yêu cầu đầu bài.
- Từ nhận xét trên ta sử dụng thuật toán tìm kiếm nhị phân theo độ dài xâu con cần tìm.
- Mỗi xâu con độ dài l, ta sẽ tính mã hash của tất cả các xâu con lưu vào mảng.
- Nếu mảng vừa tạo tồn tại ít nhất 2 phần tử giống nhau tức là tồn tại ít nhất 2 xâu con có
độ dài l thỏa mãn.
- Tìm l có độ dài dài nhất thỏa.
- Độ phức tạp của thuật toán Nlog2(N)
Chương trình viết bằng C++
#include <fstream>
#include <string>

#include <algorithm>
using namespace std;
const long long base=1000000000+11;
ifstream fi("substr.inp");
ofstream fo("substr.out");
long n;
char s[200005];
long long hash[200005],POW[200005],a[200005];
long long gethash(long i, long j){
return (hash[j]-hash[i-1]*POW[j-i+1]+
base*base)% base;
}
bool kt(long x){
for (long i=1;i<=n-x+1;i++)
a[i-1]=gethash(i,i+x-1);
sort(a,a+n-x+1);
for (long i=1;iif (a[i]==a[i-1])return 1;


}
return 0;
}
int main(){
fi>>n;
for (long i=1;i<=n;i++) fi>>s[i];
POW[0]=1;
for (long i=1;i<=n;i++)
POW[i]=(POW[i-1]*26) % base;
hash[0]=0;

for (long i=1;i<=n;i++)
hash[i]=(hash[i-1]*26+s[i]-'a')% base;
long l=1,r=n,g;
while (lg=(l+r+1)/2;
if (kt(g)) l=g;
else r=g-1;
}
if (kt(r)) fo<else fo<<0;
return 0;
}


Bài 4 - DTKSUB – Tìm chuỗi con dài nhất xuất hiện K lần
/>Sau những kỳ công trong những cuộc chinh phục các cấu trúc dữ liệu đặc biệt, tình bạn
giữa piratevà duyhung123abc ngày càng trở nên khăng khít. Rồi bỗng một ngày
nọ, duyhung123abc bỗng ra đi không một lời từ biệt, chỉ để lại một mẫu giấy cho pirate. Mẩu
giấy viết rằng : "Em ơi, anh còn nặng nợ toán lý hóa anh, chưa thể một lòng theo đuổi tin học.
Em hãy làm nốt công việc mà anh em ta còn dang dở !". pirate đọc xong, nước mắt giàn giụa.
Nếu khi hai người gặp nhau, vui sướng như khi Engels gặp Marx, thì trong giây phút chia ly này,
lòng pirate đau đớn như khi Đỗ Phủ tiễn người tri kỉ Lý Bạch lên đường.
Mất đi người anh cả, pirate như con thuyền mất phương hướng. Cuối cùng, sau những đêm
không ngủ, anh quyết định rằng mình sẽ đợi cho đến khi duyhung123abc trả xong nợ công danh
và quay trở về sẽ tiếp tục nghiên cứu các cấu trúc dữ liệu đặc biệt. Còn bây giờ, anh ta sẽ đi một
con đường mới, đi vào một thế giới mới, thế giới của các THUẬT TOÁN VỀ CHUỖI. Tuy cô độc
một mình, nhưng với niềm tin của mình, pirate đã lên đường ngay mà không có chút do dự.
Nhưng trớ trêu thay, vạn sự khởi đầu nan. Thử thách đầu tiên mà con người trẻ tuổi này gặp
phải thật đau đầu. Anh ta được cho trước một chuỗi S có độ dài N và một số K. Thử thách được
hoàn thành chỉ khi anh ấy đưa ra được độ dài của chuỗi dài nhất xuất hiện ít nhất K lần trong

chuỗi S. Làm sao đây ! Vừa vực dậy sau một cú sốc lớn, pirate rất cần sự giúp đỡ của các bạn để
không mất đi sự nhiệt huyết của mình !
Input
Dữ liệu vào gồm 2 dòng:
 Dòng 1: Hai số nguyên N và K (1 ≤ N ≤ 50000; 1 ≤ K ≤ 200).
 Dòng 2: Chuỗi S có độ dài N (gồm các chữ cái in thương viết liên tiếp nhau).
Output
Dữ liệu ra gồm một dòng duy nhất là độ dài của chuỗi dài nhất xuất hiện ít nhất K lần trong
chuỗi S.
Example
Input:
52
xxxxx
Output:
4
- Lưu ý: Một chuỗi A[1..m] được gọi là xuất hiện trong chuỗi B[1..n] K lần khi và chỉ khi tồn
tại K vị trí i phân biệt sao cho A[1..m] = B[i..i+m-1].
Hướng dẫn:
- Giải thuật giống bài 3, thay vì tìm xâu con dài nhất xuất hiện ít nhất hai lần bằng xâu con
xuất hiện ít nhất k lần.
- Độ phức tạp của thuật toán Nlog2(N)
Chương trình viết bằng C++
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const long long base=1000000000+7;
long n,k;
char s[50005];
long long hash[50005],POW[50005],a[50005];

long long gethash(long i, long j){
return (hash[j]-hash[i-1]*POW[j-i+1]+


base*base)% base;

}
bool kt(long x){
for (long i=1;i<=n-x+1;i++)
a[i-1]=gethash(i,i+x-1);
sort(a,a+n-x+1);
long count=1,max=1;
for (long i=1;i(a[i]==a[i-1])? (count++):(count=1);
if (max}
if (max>=k) return 1;
return 0;
}
int main(){
cin>>n>>k;
for (long i=1;i<=n;i++) cin>>s[i];
POW[0]=1;
for (long i=1;i<=n;i++)
POW[i]=(POW[i-1]*26) % base;
hash[0]=0;
for (long i=1;i<=n;i++)
hash[i]=(hash[i-1]*26+s[i]-'a')% base;
long l=1,r=n,g;
while (l

g=(l+r+1)/2;
if (kt(g)) l=g;
else r=g-1;
}
if (kt(r)) cout<else cout<<0;
cin.get();
return 0;
}


Bài 5 - Palindrome dài nhất – PALINY
/>Cho xâu S. Tìm xâu đối xứng dài nhất gồm các kí tự liên tiếp trong S
Input
Dòng 1: N (số ký tự của xâu S; N<=50 000)
Dòng 2: Xâu ký tự độ dài N
Output
1 dòng duy nhất gồm độ dài của xâu đối xứng dài nhất
Example
Input:
5
abacd
Hướng dẫn:
- Nhận xét:
+ Nếu tồn tại một xâu con đối xứng dài nhất độ dài chẵn l xuất hiện trong xâu s.
+ Tồn tại một xâu con đối xứng có độ dài l-2 xuất hiện trong s và không tồn tại xâu con có
độ dài l+2 thỏa mãn yêu cầu đầu bài.
- Từ nhận xét trên ta sử dụng thuật toán tìm kiếm nhị phân theo độ dài chẵn xâu con cần
tìm.
- Tương tự ta xét với độ dài xâu con độ dài lẻ.

- Độ phức tạp của thuật toán Nlog(N)
Chương trình viết bằng C++
#include <iostream>
#include <string>
#include <algorithm>
const long long base=1000000000+7;
using namespace std;
long n,res;
char s[50002],t[50002];
long long hashs[50002],hasht[50002],POW[50002];
long long gethash(long long x[],long a, long b){
return (x[b]-x[a-1]*POW[b-a+1]+base*base)%base;
}
bool ok(long k){
for (long i=1;i<=n-k+1;i++)
if (gethash(hashs,i,i+k-1)==gethash(hasht,n-k-i+2,n-i+1)) return 1;
return 0;
}
int main(){
cin>>n;
for (long i=1;i<=n;i++){
cin>>s[i];
t[n+1-i]=s[i];
}
//tinh hash xau s
hashs[0]=0;
hasht[0]=0;
for (long i=1;i<=n;i++){
hashs[i]=(hashs[i-1]*26 + s[i]-'a')% base;



hasht[i]=(hasht[i-1]*26 + t[i]-'a')% base;

}
POW[0]=1;
for (long i=1;i<=n;i++)
POW[i]=POW[i-1]*26 % base;
//tim kiem nhi phan do dai le
long l=1,r=n,mid;
while (lmid=(l+r+1)/2;
if (mid % 2==0) mid++;
if (ok(mid)) l=mid;
else r=mid-2;
}
res=r;
//tim kiem nhi phan do dai chan
l=1;r=n;
while (lmid=(l+r+1)/2;
if (mid %2!=0) mid++;
if (ok(mid)) l=mid;
else r=mid-2;
}
if (rescout<cin.get();

}
5. Tổng kết:

a. Thuật toán:
Ý tưởng thuật toán Hash dựa trên việc đổi từ hệ cơ số lớn sang hệ thập phân, so sánh hai số
thập phân lớn bằng cách so sánh phần dư của chúng với một số đủ lớn.
b. Ưu điểm:
Ưu điểm của thuật toán Hash là cài đặt rất dễ dàng. Linh động trong ứng dụng và có thể thay thế
các thuật toán chuẩn ‘hầm hố’ khác.
c. Nhược điểm:
Nhược điểm của thuật toán Hash là tính chính xác. Mặc dù rất khó sinh test để có thể làm cho
thuật toán chạy sai, nhưng không phải là không thể. Vì vậy, để nâng cao tính chính xác của thuật
toán, người ta thường dùng nhiều modulo khác nhau để so sánh mã Hash (ví dụ như dùng 3
modulo một lúc).


CÁC PHỤ LỤC KÈM THEO SÁNG KIẾN
Tài liệu tham khảo
- Hash: Thuật toán so khớp xâu - Le Khac Minh Tue
- />- />- />- />


×