Thuật toán khớp chuỗi k không khớp nhanh


10

Tôi đang tìm kiếm một thuật toán khớp chuỗi k-không khớp nhanh. Cho một chuỗi mẫu P có độ dài m và chuỗi văn bản T có độ dài n, tôi cần một thuật toán nhanh (thời gian tuyến tính) để tìm tất cả các vị trí trong đó P khớp với một chuỗi con của T với tối đa k không khớp. Điều này khác với vấn đề khác biệt k (chỉnh sửa khoảng cách). Một sự không phù hợp ngụ ý chuỗi con và mẫu có một chữ cái khác nhau ở hầu hết các vị trí k. Tôi thực sự chỉ yêu cầu k = 1 (nhiều nhất là 1 không khớp), vì vậy một thuật toán nhanh cho trường hợp cụ thể của k = 1 cũng sẽ đủ. Kích thước bảng chữ cái là 26 (văn bản tiếng Anh không phân biệt chữ hoa chữ thường), do đó, yêu cầu không gian không nên tăng quá nhanh với kích thước của bảng chữ cái (ví dụ: thuật toán FAAST, tôi tin rằng, chiếm không gian theo kích thước của bảng chữ cái, và vì vậy chỉ phù hợp cho trình tự protein và gen).

Một cách tiếp cận dựa trên lập trình động sẽ có xu hướng là O (mn) trong trường hợp xấu nhất, sẽ quá chậm. Tôi tin rằng có những sửa đổi của thuật toán Boyer-Moore cho việc này, nhưng tôi không thể có được những bài báo như vậy. Tôi không có đăng ký để truy cập các tạp chí học thuật hoặc ấn phẩm, vì vậy mọi tài liệu tham khảo sẽ phải thuộc phạm vi công cộng.

Tôi sẽ đánh giá rất cao bất kỳ con trỏ, hoặc tham chiếu đến các tài liệu có sẵn miễn phí, hoặc chính thuật toán cho vấn đề này.


2
Nếu mẫu được cố định (nhưng văn bản phù hợp khác nhau), bạn có khả năng có thể tạo một máy tự động hữu hạn và chạy văn bản thông qua đó. Ngoài ra còn có các thuật toán sử dụng cây hậu tố (thường là tốt nếu văn bản không đổi và mẫu khác nhau, nhưng cũng có thể áp dụng nếu cả hai thay đổi), bạn có thể tìm thấy một số tài liệu tham khảo trên web. (Chưa thêm câu trả lời vì tôi không chắc lắm về thuật toán dựa trên cây hậu tố, nếu ai đó biết xin vui lòng bỏ qua nhận xét này).
Aryabhata

@Aryabhata Cảm ơn! Cả mẫu và văn bản đều thay đổi. Trong bối cảnh đó, việc xây dựng một máy tự động hữu hạn sẽ quá tốn kém, đặc biệt là khi bao gồm phạm vi cho 1 sự không phù hợp. Đối với các mảng hậu tố / mảng hậu tố, tôi chưa bao giờ sử dụng chúng và biết rất ít về chúng, nhưng có ấn tượng rằng chúng chậm xây dựng và hiệu quả chủ yếu để khớp chính xác. Nhưng tôi sẽ khám phá tùy chọn này hơn nữa. Bất kỳ con trỏ theo hướng này, hoặc theo bất kỳ hướng nào khác sẽ hữu ích nhất!
Paresh

1
Không, cây hậu tố cũng có thể được sử dụng cho các kết quả gần đúng. Toàn bộ wiki tuyên bố như vậy: en.wikipedia.org/wiki/Suffix_tree
Aryabhata

Câu trả lời:


5

Mảng hậu tố có thể được sử dụng cho vấn đề này. Chúng chứa các vị trí bắt đầu của mỗi hậu tố của chuỗi được sắp xếp theo thứ tự từ điển. Mặc dù chúng có thể được xây dựng một cách ngây thơ trong phức tạp, có những phương pháp để xây dựng chúng trong Θ ( n ) phức tạp. Xem ví dụ nàynày . Hãy để chúng tôi gọi mảng hậu tố SA.O(nlogn)Θ(n)

Khi mảng hậu tố đã được xây dựng, chúng ta cần xây dựng mảng Tiền tố chung dài nhất (LCP) cho mảng hậu tố. Mảng LCP lưu trữ độ dài của tiền tố chung dài nhất giữa hai tiền tố liên tiếp trong mảng hậu tố (hậu tố từ vựng liên tiếp). Do đó, LCP [i] chứa độ dài của tiền tố phổ biến dài nhất giữa SA [i] và SA [i + 1]. Mảng này cũng có thể được xây dựng trong thời gian tuyến tính: xem ở đây , ở đâyở đây để biết một số tài liệu tham khảo tốt.

Bây giờ, để tính độ dài của tiền tố dài nhất phổ biến cho bất kỳ hai hậu tố nào trong cây hậu tố (thay vì các hậu tố liên tiếp), chúng ta cần sử dụng một số cấu trúc dữ liệu của RMQ . Nó đã được hiển thị trong các tham chiếu ở trên (và có thể dễ dàng nhìn thấy nếu mảng được hiển thị dưới dạng cây hậu tố), rằng độ dài của tiền tố chung dài nhất giữa hai hậu tố có vị trí v ( u < v ) trong mảng hậu tố , có thể thu được là m i n u < = k < = v - 1 L C P [ k ]uvu<vminu<=k<=v1LCP[k]. Một RMQ tốt có thể xử lý trước mảng trong thời gian O ( n ) hoặc O ( n log n ) và trả lời các truy vấn có dạng L C P [ u , v ] trong thời gian O ( 1 ) . Xem ở đây để biết thuật toán RMQ succint và ở đây để có hướng dẫn tốt về RMQ, và mối quan hệ (và mức giảm) giữa LCA và RMQ. Điều này có một cách tiếp cận thay thế tốt đẹp.LCPO(n)O(nlogn)LCP[u,v]O(1)

Với thông tin này, chúng tôi xây dựng mảng hậu tố và các mảng liên quan (như được mô tả ở trên) để nối hai chuỗi với một dấu phân cách ở giữa (chẳng hạn như T # P, trong đó '#' không xảy ra trong một trong hai chuỗi). Sau đó, chúng ta có thể thực hiện khớp chuỗi k không khớp bằng phương pháp "kangaroo". Điều nàyđiều này giải thích phương pháp kangaroo trong bối cảnh của cây hậu tố, nhưng cũng có thể được áp dụng trực tiếp cho mảng hậu tố. Với mọi chỉ số của văn bản T , hãy tìm L C P của hậu tố T bắt đầu từ i và hậu tố của PiTLCPTiPbắt đầu từ 0. Điều này cho biết vị trí sau đó xảy ra sự không khớp đầu tiên khi khớp với T [ i ] . Đặt độ dài này là l 0 . Bỏ qua ký tự không khớp trong cả TP và cố gắng khớp các chuỗi còn lại. Nghĩa là, một lần nữa tìm L C P của T [ i + l 0 + 1 ]P [ l 0 + 1 ] . Lặp lại điều này cho đến khi bạn có được k không khớp hoặc kết thúc chuỗi. MỗiPT[i]l0TPLCPT[i+l0+1]P[l0+1]k O ( 1 ) . Có O ( k ) L C P 'cho mỗi chỉ số i của T , tạo ra tổng độ phức tạp của O ( n k ) .LCPO(1)O(k) LCPiTO(nk)

Tôi đã sử dụng một cách dễ dàng hơn để thực hiện RMQ với tổng độ phức tạp của log hoặc O ( n k + n log n ) nếu m = O ( n ) , nhưng nó có thể được thực hiện trong O ( n k ) như mô tả ở trên. Có thể có các phương pháp trực tiếp khác cho vấn đề này, nhưng đây là một cách tiếp cận mạnh mẽ và chung chung có thể được áp dụng cho rất nhiều vấn đề tương tự.O(nk+(n+m)log(n+m))O(nk+nlogn)m=O(n)O(nk)


Tuyệt quá! Tôi có một số đọc trên danh sách TODO của tôi bây giờ :-)
Aryabhata

Liên kết siam.org trong đoạn thứ hai bị hỏng, nhưng giấy được liên kết có thể được tìm thấy ở đây epub.siam.org/doi/pdf/10.1137/1.9781611972917.3
leecbaker

4

Dưới đây là một thuật toán dự kiến ( có thể được mở rộng sang k khác , làm cho nó trở thành O ( n k + m ) ). (Tôi đã không thực hiện các tính toán để chứng minh rằng nó là như vậy, mặc dù).O(n+m)kO(nk+m)

Ý tưởng này tương tự như thuật toán băm cán Rabin-Karp cho các kết hợp chuỗi con chính xác.

Ý tưởng là để tách mỗi chuỗi có độ dài vào 2 k khối m / 2 k kích thước mỗi và tính toán hash lăn cho mỗi khối (cho 2 k giá trị hash) và so sánh những 2 k giá trị băm chống lại một từ mô hình.m2km/2k2k2k

Chúng tôi cho phép tối đa không khớp trong các giá trị đó.k

Nếu có nhiều hơn không khớp xảy ra, chúng tôi từ chối và tiếp tục. Nếu không, chúng tôi cố gắng và xác nhận một trận đấu gần đúng.k

Tôi mong đợi (báo trước: tôi đã không thử nó) điều này có thể sẽ nhanh hơn trong thực tế và có thể dễ dàng viết mã / bảo trì hơn là sử dụng cách tiếp cận dựa trên cây hậu tố.


Chỉ cần làm rõ. Bằng cách "phân tách từng chuỗi có độ dài m thành 2k khối có kích thước m / 2k mỗi ...", bạn có nghĩa là tách từng chuỗi con có độ dài m trong T (có độ dài n) thành các khối 2k. Và hàm băm này có thể được tính bằng O (n) bằng phương pháp băm cán. Sau đó, chuỗi mẫu cũng sẽ được chia thành các khối 2k và các giá trị băm tương ứng sẽ được so sánh, cho phép tối đa các khối k không khớp. Nếu vậy, thì chúng ta sẽ có khả năng loại bỏ tất cả các trường hợp trong đó số lượng không khớp nhiều hơn k. Tôi đã hiểu đúng chưa?
Paresh

kΩ(nk)O(n)

Tôi thích cách tiếp cận này! Tuy nhiên, cách tiếp cận này nói chung là nhanh, nhưng suy giảm thành O (mnk) nếu số lượng trận đấu cao (O (n) khớp). Giữ điều này trong tâm trí, tôi duy trì hai băm lăn, với giả định rằng cả hai không thể có xung đột cho cùng một đầu vào (tôi đã không làm điều này một cách toán học vì tôi muốn xem tốc độ). Bằng cách này, chúng tôi không phải xác minh kết quả trùng khớp nếu hai giá trị băm đồng ý. Điều này nói chung là khá nhanh, nhưng điều này cũng quá chậm nếu số lượng trận đấu lớn. Với điều này và với cách bạn đề xuất, thật chậm cho các trận đấu lớn.
Paresh

mm/2kmO(nkm)

mm/2k2kk+1k+cΩ(nm)mm/2k
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.