Tìm kiếm mẫu (pattern matching) bằng Finite Automaton (FA) tiền xử lý mẫu để xây dựng bảng chuyển trạng thái (transition table), sau đó quét văn bản trong O(n) thời gian — nhanh hơn KMP khi alphabet nhỏ và nhiều lần tìm kiếm cùng mẫu.
Ý tưởng
- Xây dựng DFA (Deterministic Finite Automaton) nhận diện mẫu
P[0..m-1]. - Trạng thái
q= độ dài tiền tố khớp nhất củaPlà suffix của văn bản đã đọc. - Bảng chuyển trạng thái
TF[q][ch]= trạng thái tiếp theo khi đọc ký tựchở trạng tháiq. - Trạng thái chấp nhận:
q == m(tìm thấy mẫu).
Xây dựng bảng chuyển trạng thái (O(m * |Σ|))
void buildTF(const string& pat, vector>& TF) {
int m = pat.size();
TF.assign(m + 1, array{});
// Trạng thái 0
for (int ch = 0; ch < 256; ++ch) TF[0][ch] = 0;
TF[0][(unsigned char)pat[0]] = 1;
int lps = 0; // Longest Prefix Suffix
for (int i = 1; i <= m; ++i) {
// Copy từ trạng thái lps
for (int ch = 0; ch < 256; ++ch) TF[i][ch] = TF[lps][ch];
// Cập nhật cho ký tự pat[i]
if (i < m) TF[i][(unsigned char)pat[i]] = i + 1;
// Cập nhật lps cho lần sau
if (i < m) lps = TF[lps][(unsigned char)pat[i]];
}
}
Tìm kiếm (O(n))
void searchFA(const string& text, const string& pat, const vector>& TF) {
int state = 0;
for (int i = 0; i < text.size(); ++i) {
state = TF[state][(unsigned char)text[i]];
if (state == pat.size()) {
cout << "Tim thay tai vi tri: " << (i - pat.size() + 1) << '\n';
}
}
}
Chương trình hoàn chỉnh
#include <bits/stdc++.h>
using namespace std;
const int ALPHA = 256; // ASCII mở rộng
void buildTransitionTable(const string& pat, vector>& TF) {
int m = pat.size();
TF.assign(m + 1, array{});
// State 0
TF[0].fill(0);
TF[0][(unsigned char)pat[0]] = 1;
int lps = 0;
for (int i = 1; i <= m; ++i) {
// Copy từ lps
for (int ch = 0; ch < ALPHA; ++ch) TF[i][ch] = TF[lps][ch];
// Cập nhật ký tự hiện tại
if (i < m) TF[i][(unsigned char)pat[i]] = i + 1;
// Cập nhật lps
if (i < m) lps = TF[lps][(unsigned char)pat[i]];
}
}
void finiteAutomatonSearch(const string& text, const string& pat) {
vector> TF;
buildTransitionTable(pat, TF);
int state = 0;
for (int i = 0; i < text.size(); ++i) {
state = TF[state][(unsigned char)text[i]];
if (state == pat.size()) {
cout << "Pattern found at index: " << (i - pat.size() + 1) << '\n';
}
}
}
int main() {
string text = "AABAA ABBAACCDD CCDDAABAA";
string pattern = "AABAA";
cout << "Text: " << text << '\n';
cout << "Pattern: " << pattern << '\n';
finiteAutomatonSearch(text, pattern);
return 0;
}
Kết quả
Text: AABAA ABBAACCDD CCDDAABAA Pattern: AABAA Pattern found at index: 0 Pattern found at index: 20
So sánh các thuật toán tìm mẫu
| Thuật toán | Tiền xử lý | Tìm kiếm | Không gian | Đặc điểm |
|---|---|---|---|---|
| Naive | O(1) | O(n*m) | O(1) | Đơn giản, chậm |
| KMP | O(m) | O(n) | O(m) | Không cần bảng lớn, phổ biến |
| Finite Automaton | O(m*|Σ|) | O(n) | O(m*|Σ|) | Rất nhanh khi tìm nhiều lần cùng pattern, alphabet nhỏ |
| Boyer-Moore | O(m+|Σ|) | O(n) avg, O(n*m) worst | O(|Σ|) | Nhanh thực tế, nhảy lớn |
| Rabin-Karp | O(m) | O(n+m) avg | O(1) | Hash rolling, tìm nhiều pattern |