Sunday
1. Giới thiệu về thuật toán Sunday
"Sunday algorithm" là một thuật toán tìm kiếm chuỗi con trong chuỗi, được Daniel M.Sunday đề xuất vào năm 1990.
Ý tưởng của thuật toán Sunday: Đối với chuỗi văn bản
T
và chuỗi mẫup
đã cho, trước tiên ta tiền xử lý chuỗi mẫup
. Sau đó, trong quá trình so khớp, khi gặp một ký tự trong chuỗi văn bảnT
không khớp với chuỗi mẫup
, dựa trên chiến lược khởi đầu, ta có thể bỏ qua một số trường hợp không khớp và dịch chuyển chuỗi mẫu sang phải một số vị trí.
Ý tưởng của thuật toán Sunday tương tự như thuật toán Boyer Moore. Tuy nhiên, khác biệt của Sunday algorithm là việc so khớp diễn ra từ trái sang phải và khi chuỗi mẫu p
không khớp, ta quan tâm đến ký tự tiếp theo trong chuỗi văn bản T
tham gia so khớp. Khi gặp một ký tự không khớp trong chuỗi văn bản T
, ta có thể di chuyển chuỗi mẫu p
sang phải nhanh chóng.
Khi gặp ký tự không khớp, ta có thể di chuyển nhanh sang phải dựa trên hai trường hợp sau:
- Trường hợp 1: Ký tự tiếp theo của chuỗi văn bản
T
sau ký tự cuối cùng của chuỗi mẫup[m - 1]
xuất hiện trong chuỗi mẫup
.- Trong trường hợp này, ta có thể căn chỉnh chuỗi mẫu
p
sao cho ký tựT[i + m]
trùng với vị trí cuối cùng xuất hiện của nó trong chuỗi mẫup
, như hình dưới đây. - Số vị trí di chuyển sang phải = Vị trí tiếp theo của ký tự cuối cùng của chuỗi văn bản
T
- Vị trí xuất hiện cuối cùng của ký tựT[i + m]
trong chuỗi mẫup
. - Lưu ý: Vị trí tiếp theo của ký tự cuối cùng của chuỗi văn bản
T
thực chất là "độ dài chuỗi mẫu".
- Trong trường hợp này, ta có thể căn chỉnh chuỗi mẫu
- Trường hợp 2: Ký tự tiếp theo của chuỗi văn bản
T
sau ký tự cuối cùng của chuỗi mẫup[m - 1]
không xuất hiện trong chuỗi mẫup
.- Trong trường hợp này, ta có thể di chuyển toàn bộ chuỗi mẫu sang phải, như hình dưới đây.
- Số vị trí di chuyển sang phải = Độ dài toàn bộ chuỗi mẫu + 1.
2. Các bước của thuật toán Sunday
Các bước của thuật toán Sunday được mô tả như sau:
- Tính độ dài của chuỗi văn bản
T
làn
và độ dài của chuỗi mẫup
làm
. - Tiến hành tiền xử lý chuỗi mẫu
p
và tạo bảng di chuyểnbc_table
. - Căn chỉnh phần đầu của chuỗi mẫu
p
với chuỗi văn bảnT
, đặti
trỏ đến vị trí bắt đầu của chuỗi văn bản, tức lài = 0
.j
trỏ đến vị trí bắt đầu của chuỗi mẫu, tức làj = 0
, sau đó so sánh từ vị trí bắt đầu của chuỗi mẫu.- Nếu ký tự tại vị trí
T[i + j]
của chuỗi văn bảnT
trùng với ký tự tại vị tríp[j]
của chuỗi mẫup
, tiếp tục so sánh ký tự tiếp theo.- Nếu đã so khớp hết toàn bộ chuỗi mẫu, trả về vị trí bắt đầu của chuỗi mẫu
p
trong chuỗi văn bảnT
.
- Nếu đã so khớp hết toàn bộ chuỗi mẫu, trả về vị trí bắt đầu của chuỗi mẫu
- Nếu ký tự tại vị trí
T[i + j]
của chuỗi văn bảnT
không trùng với ký tự tại vị tríp[j]
của chuỗi mẫup
, ta thực hiện:- Dựa trên bảng di chuyển
bc_table
và ký tựT[i + m]
tương ứng với vị trí cuối cùng của chuỗi mẫu trong chuỗi văn bảnT
, tính toán số vị trí có thể di chuyểnbc_table[T[i + m]]
, sau đó di chuyển chuỗi mẫu sang phải.
- Dựa trên bảng di chuyển
- Nếu ký tự tại vị trí
- Nếu di chuyển đến cuối cùng mà không tìm thấy trường hợp khớp, trả về
-1
.
3. Cài đặt thuật toán Sunday
3.1 Cài đặt bảng di chuyển
Cài đặt bảng di chuyển khá đơn giản, tương tự như cách tạo bảng di chuyển trong thuật toán Horspool. Các bước cụ thể như sau:
- Sử dụng một bảng băm
bc_table
,bc_table[bad_char]
đại diện cho số lượng vị trí có thể di chuyển sang phải khi gặp ký tự không phù hợp. - Duyệt qua chuỗi mẫu
p
, với mỗi ký tự hiện tạip[i]
làm khóa, số lượng vị trí có thể di chuyển sang phải (m - i
) làm giá trị và lưu vào bảng băm. Nếu có ký tự trùng lặp, giá trị mới sẽ ghi đè lên giá trị đã lưu trước đó. Kết quả là bảng băm sẽ lưu trữ số lượng vị trí có thể di chuyển sang phải từ vị trí cuối cùng xuất hiện của ký tự trong chuỗi mẫu.
Đoạn mã dưới đây là cài đặt của việc tạo bảng di chuyển:
# Tạo bảng di chuyển
# bc_table[bad_char] đại diện cho số lượng vị trí có thể di chuyển sang phải khi gặp ký tự không phù hợp
def generateBadCharTable(p: str):
m = len(p)
bc_table = dict()
for i in range(m): # Duyệt đến vị trí cuối cùng m - 1
bc_table[p[i]] = m - i # Cập nhật số lượng vị trí có thể di chuyển sang phải khi gặp ký tự không phù hợp
return bc_table
3.2 Cài đặt toàn bộ thuật toán Sunday
# Thuật toán Sunday, T là chuỗi văn bản, p là chuỗi mẫu
def sunday(T: str, p: str) -> int:
n, m = len(T), len(p)
bc_table = generateBadCharTable(p) # Tạo bảng di chuyển
i = 0
while i <= n - m:
j = 0
if T[i: i + m] == p:
return i # Kết thúc so khớp, trả về vị trí của chuỗi mẫu p trong chuỗi văn bản T
if i + m >= n:
return -1
i += bc_table.get(T[i + m], m + 1) # Di chuyển sang phải nhanh chóng dựa trên bảng di chuyển
return -1 # Không tìm thấy khớp
# Tạo bảng di chuyển
# bc_table[bad_char] đại diện cho số lượng vị trí có thể di chuyển sang phải khi gặp ký tự không phù hợp
def generateBadCharTable(p: str):
m = len(p)
bc_table = dict()
for i in range(m): # Duyệt đến vị trí cuối cùng m - 1
bc_table[p[i]] = m - i # Cập nhật số lượng vị trí có thể di chuyển sang phải khi gặp ký tự không phù hợp
return bc_table
print(sunday("abbcfdddbddcaddebc", "aaaaa"))
print(sunday("abbcfdddbddcaddebc", "bcf"))
4. Phân tích thuật toán Sunday
- Trong trường hợp trung bình, thuật toán Sunday có độ phức tạp thời gian là O(n), tuy nhiên trong trường hợp xấu nhất, độ phức tạp thời gian có thể là O(n * m).