Chương 3
Chuỗi
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 1
Nội dung
Ðịnh nghĩa chuỗi.
Các cách phân chia chuỗi.
Các hàm và thủ tục.
Tìm kiếm chuỗi con.
f Giải
thuật Brute-Force
f Giải thuật Knuth-Morris-Pratt
f Giải thuật Boyer-Moore
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 2
Các định nghĩa
Định nghĩa chuỗi
f Chuỗi
(string) là một dãy các ký tự được
chứa trong một vùng liên tục của bộ nhớ.
Các ký tự này có thể là các ký tự chữ, ký tự
số hoặc các ký tự đặc biệt.
Các khái niệm
f Chiều
dài (length) của chuỗi là số ký tự của
chuỗi.
f Chuỗi rỗng (null string) là chuỗi có chiều dài
bằng 0, ký hiệu là ‘’.
f Chuỗi con (substring) là một thành phần bao
gồm các ký tự liên tiếp nhau của chuỗi.
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 3
Các cách phân chia chuỗi
Sử dụng ký tự đặc biệt
f Sử
dụng các ký tự đặc biệt (không là ký tự
của chuỗi) để phân tách các chuỗi con.
f Chiều dài của các chuỗi con có thể khác
nhau.
f Truy xuất tuần tự từ đầu chuỗi để tìm kiếm
một chuỗi con.
f Thời gian truy xuất một chuỗi con là chậm.
800
H O A N G
Chương 3. Chuỗi
806
A N H
810
817
P H U O N G
2006
L O C
Nguyễn Trung Trực - Khoa CNTT 4
Các cách phân chia chuỗi
Sử dụng chiều dài chuỗi con cố định
f Sử
dụng các ký đệm vào mỗi chuỗi con.
f Chiều dài của các chuỗi con bằng nhau.
f Truy xuất trực tiếp theo công thức tính địa
chỉ để tìm kiếm một chuỗi con.
f Thời gian truy xuất một chuỗi con là nhanh.
800
H
807
O A
N
Chương 3. Chuỗi
G
A
814
N
H
P
821
H
U
2006
O
N
G
L
O
C
Nguyễn Trung Trực - Khoa CNTT 5
Các cách phân chia chuỗi
Sử dụng con trỏ
f Sử
dụng các con trỏ chỉ vào các chuỗi con.
f Chiều dài của các chuỗi con có thể khác
nhau.
f Truy xuất trực tiếp theo con trỏ để tìm kiếm
một chuỗi con.
f Thời gian truy xuất một chuỗi con là nhanh.
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 6
Các cách phân chia chuỗi
Sử dụng con trỏ đầu
last
H O A N G A N H P H U O N G L O C
pointer
1
800
2
805
3
4
808
814
ai = pointer[i]
bi = pointer[i+1] - 1 với i < n
= last
Chương 3. Chuỗi
với i = n
2006
Nguyễn Trung Trực - Khoa CNTT 7
Các cách phân chia chuỗi
Sử dụng con trỏ cuối
first
H O A N G A N H P H U O N G L O C
pointer
1
804
2
807
3
4
813
816
ai = first
= pointer[i-1] +1 với i = 1
bi = pointer[i]
Chương 3. Chuỗi
với i > 1
2006
Nguyễn Trung Trực - Khoa CNTT 8
Các hàm và thủ tục
Các hàm
f length(s):
y
Ví dụ: length(‘abc’) → 3
f concat(s1,
…, sn.
y
trả về số ký tự của chuỗi s.
s2, …, sn): kết nối các chuỗi s1, s2,
Ví dụ: concat(‘abc’,’12’) → ‘abc12’
f copy(s,
m, n): lấy chuỗi con của chuỗi s gồm
n ký tự kể từ ký tự thứ m.
y
Ví dụ: copy(‘abcde’,3,2) → ‘cd’
f pos(s1,
s2): trả về vị trí tìm thấy chuỗi s1 trong
chuỗi s2.
y
Ví dụ: pos(‘bc’,’abcd’) → 2
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 9
Các hàm và thủ tục
Các thủ tục
f val(s,
x, n): biến đổi chuỗi s thành giá trị số
và gán cho x; n được gán số 0 nếu biến đổi
đúng.
y
y
Ví dụ: val(‘123.45’, x, n) sẽ gán số 123.45 cho x.
Ví dụ: val(’12a’, x, n) → x không xác định và n là
3.
f str(x,
s): biến đổi giá trị số x thành giá trị
chuỗi và gán cho s.
y
Ví dụ: str(123.45, s) sẽ gán chuỗi ‘123.45’ cho s.
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 10
Các hàm và thủ tục
Các thủ tục
f delete(s,
m, n): xóa n ký tự trong chuỗi s bắt
đầu từ vị trí m.
y
Ví dụ: chuỗi s là ‘abcde’ thì delete(s,2,3) sẽ gán
chuỗi ‘ae’ cho s.
f insert(s1,
s2, m): xen chuỗi s1 vào trong chuỗi
s2 tại vị trí m.
y
Ví dụ: chuỗi s là ‘abc’ thì insert(‘12’,s,2) sẽ gán
chuỗi ‘a12bc’ cho s.
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 11
Tìm kiếm chuỗi con
Tìm chuỗi p có chiều dài là m trong chuỗi a
có chiều dài là n. Có hai trường hợp tìm
kiếm:
f Tìm
thấy chuỗi p trong chuỗi a: kết quả trả về
là vị trí của ký tự đầu tiên của lần tìm thấy
đầu tiên.
f Không tìm thấy chuỗi p trong chuỗi a: kết
quả trả về là 0.
Các giải thuật
f Giải
thuật Brute-Force
f Giải thuật Knuth-Morris-Pratt
f Giải thuật Boyer-Moore
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 12
Giải thuật Brute-Force
Phương pháp
fỞ
vị trí ký tự a[i], so sánh các ký tự tương
ứng từ trái qua phải: p[1] với a[i], p[2] với
a[i+1], …, p[m] với a[i+m-1].
a
x x x x x x x x x x
i
p
x x x x x x
1
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 13
Giải thuật Brute-Force
function Brute_Force (p, a: string): integer;
var i, j, m, n: integer;
begin
m := length(p); n := length(a); i := 1; j := 1;
repeat
if a[i] = p[j] then begin
i := i + 1; j := j + 1
end
else begin
i := i - j + 1; j := 1
end
until (j > m) or (i > n);
if j > m then Brute_Force := i - m
else Brute_Force := 0
end;
Lệnh gọi: position := Brute_Force (p, a);
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 14
Giải thuật Knuth-Morris-Pratt
Phương pháp
f Trong
giải thuật Brute-Force, khi so sánh để
p[j] khác với a[i] thì đã có j-1 ký tự đầu tiên
của chuỗi p trùng với các ký tự tương ứng
của chuỗi a.
f Do đó, không cần phải cho i quay trở về mà
vẫn cho i tăng tiếp tục (thay thế i := i - j + 2
bởi i := i + 1) và gán cho j một giá trị thích
hợp. Mảng next[j] xác định các giá trị thích
hợp gán cho j.
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 15
Giải thuật Knuth-Morris-Pratt
Xét chuỗi p = ‘10100111’
j next[j]
2 1
1 0 1 0
1 0 1
3 1
1 0 1 0
1 0
4 2
1 0 1 0
1 0
5 3
1 0 1 0
1 0
6 1
1 0 1 0
7
8
2
2
Chương 3. Chuỗi
0
0
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
1
1 0 1 0 0 1
1
1 0 1 0 0 1
1
1
1
0
1
0
1
0
1
0
1
0
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
1
1 1
1 1
1 1
0 0 1 1 1
0 0 1 1 1
1 0 0 1 1 1
2006
Nguyễn Trung Trực - Khoa CNTT 16
Giải thuật Knuth-Morris-Pratt
function KMP (p, a: string): integer;
var i, j, m, n: integer;
next: array [1..255] of integer;
procedure Init_Next;
begin
i := 1; j := 0; next[1] := 0;
repeat
if (j = 0) and (p[i] = p[j]) then
begin
i := i + 1; j := j + 1; next[i] := j
end
else j := next[j]
until i >= m;
end;
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 17
Giải thuật Knuth-Morris-Pratt
begin {KMP}
m := length(p); n := length(a); Init_Next;
i := 1; j := 1;
repeat
if (j = 0) or (a[i] = p[j]) then
begin
i := i + 1; j := j + 1
end
else j := next[j]
end
until (j > m) or (i > n);
if j > m then KMP := i - m
else KMP := 0
end;
Lệnh gọi: position := KMP (p, a);
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 18
Giải thuật Knuth-Morris-Pratt cải tiến
Chương 3. Chuỗi
1
0
1
0
0
1
1
1
1
0
1
0
0
1
1
1
2006
Nguyễn Trung Trực - Khoa CNTT 19
Giải thuật Knuth-Morris-Pratt cải tiến
procedure Init_Next;
begin
i := 1; j := 0; next[1] := 0;
repeat
if (j = 0) and (p[i] = p[j]) then
begin
i := i + 1; j := j + 1;
if p[j] <> p[i] then next[i] := j
else next[i] := next[j]
end
else j := next[j]
until i >= m;
end;
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 20
Giải thuật Boyer-Moore
Phương pháp
f Xét
chuỗi p từ phải qua trái trong khi so sánh
chuỗi p với chuỗi a.
f Next[j] là số vị trí ký tự mà chuỗi p di chuyển
qua phải đối với chuỗi p1 (bản sao của chuỗi
p) để có được vị trí khác nhau ở ký tự thứ j
kể từ phải qua trái của chuỗi p.
f Mảng skip[c] xác định vị trí mới của i khi có
sự so sánh khác nhau.
y
y
skip[c] = m nếu c không là ký tự của chuỗi p.
skip[c] = m-j nếu c là ký tự thứ j của chuỗi p.
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 21
Giải thuật Boyer-Moore
Xét chuỗi p = ‘10110101’
j next[j]
2 1
1 0 1 1 0 1 0 1
1 0 1 1
3 7
1 0 1 1 0 1 0 1
1
4 2
1 0 1 1 0 1 0 1
1 0 1 1 0 1
5 5
1 0 1 1 0 1 0 1
1 0 1
6 5
1 0 1 1 0 1 0 1
1 0 1
7 5
1 0 1 1 0 1 0 1
1 0 1
8 5
1 0 1 1 0 1 0 1
1 0 1
Chương 3. Chuỗi
0 1 0 1
0 1 1 0 1 0 1
0 1
1 0 1 0 1
1 0 1 0 1
1 0 1 0 1
1 0 1 0 1
2006
Nguyễn Trung Trực - Khoa CNTT 22
Giải thuật Boyer-Moore
function Boyer_Moore (p, a: string): integer;
var i, j, m, n: integer;
skip: array [1..255] of integer;
procedure Init_Skip;
var i: 1..255; j: integer;
begin
for i := 1 to 255 do skip[i] := m;
if skip[ord(p[j])] = m then skip[ord(p[j])] := m-j;
end;
begin
m := length(p); n := length(a); Init_Skip;
i := m; j := m;
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 23
Giải thuật Boyer-Moore
repeat
if a[i] = p[j] then
begin
i := i - 1; j := j - 1
end
else
begin
if m - j + 1 > skip[ord(a[i])] then i := i+m-j+1
else i := i + skip[(ord(a[i])];
j := m
end
until (j < 1) or (i > n);
if j < 1 then Boyer_Moore := i + 1
else Boyer_Moore := 0
end;
Chương 3. Chuỗi
2006
Nguyễn Trung Trực - Khoa CNTT 24