Thuật toán tìm kiếm chuỗi nào thực sự là nhanh nhất?


27

Tôi đã bị mắc kẹt trong một thời gian đó là thuật toán tìm kiếm chuỗi nhanh nhất, nghe nhiều ý kiến, nhưng cuối cùng tôi không chắc chắn.

Tôi đã nghe một số người nói rằng thuật toán nhanh nhất là Boyer-Moore và một số người nói rằng Knuth-Morris-Pratt thực sự nhanh hơn.

Tôi đã tìm kiếm sự phức tạp trên cả hai nhưng chúng hầu như trông giống nhau O(n+m). Tôi đã thấy rằng trong trường hợp xấu nhất Boyer-Moore có một O(nm)sự phức tạp so với Knuth-Morris-Pratt có O (m + 2 * n). Trong đó n = chiều dài của văn bản và m = chiều dài của mẫu.

Theo như tôi biết thì Boyer-Moore có trường hợp xấu nhất tuyến tính nếu tôi sử dụng Quy tắc Galil.

Câu hỏi của tôi, Trên tất cả, đây thực sự là thuật toán tìm kiếm Chuỗi nhanh nhất (Câu hỏi này bao gồm tất cả các thuật toán sting có thể có, không chỉ Boyer-Moore và Knuth-Morris-Pratt).

Chỉnh sửa: Do câu trả lời này

Điều tôi chính xác đang tìm kiếm là:

Cho một văn bản Tvà một mô hình Ptôi phải tìm tất cả các xuất hiện của Ptrong T.

Ngoài ra độ dài của P và T là từ [1,2 000 000]và chương trình phải chạy dưới 0,15 giây.

Tôi biết rằng KMP và Rabin-Karp là đủ để đạt được 100% điểm cho vấn đề nhưng tôi vì một người muốn thử và thực hiện Boyer-Moore. Cái nào sẽ là tốt nhất cho kiểu tìm kiếm này?


6
Khi bạn kiểm tra những thứ này bằng ngôn ngữ bạn chọn, bạn đã tìm thấy gì?
Walter

4
Trong một số thử nghiệm, Boyer-Moore đã tốt hơn đối với KMP khác thì tốt hơn, nhưng tôi không chắc mình có cách triển khai "tốt nhất" trong số đó. Đối với ngôn ngữ của sự lựa chọn, nó nằm trong các thẻ: C ++ (không chắc bạn đã thấy điều đó vì bạn đã viết "ngôn ngữ của sự lựa chọn"). PS Tôi cũng không chắc chắn nếu tôi đã thử nghiệm trên các bài kiểm tra tốt nhất.
vandamon taigi


Knuth-Morris-Pratt có O (m + 2 * n) ... Ý bạn là O (m + n).
Jules

Chọn một cái có độ phức tạp thuật toán tốt và sau đó tinh chỉnh crap ra khỏi nó với một trình lược tả trong tay - luôn làm việc cho tôi. :-D

Câu trả lời:


38

Nó phụ thuộc vào loại tìm kiếm bạn muốn thực hiện. Mỗi thuật toán thực hiện đặc biệt tốt cho một số loại tìm kiếm nhất định, nhưng bạn chưa nêu bối cảnh tìm kiếm của mình.

Dưới đây là một số suy nghĩ tiêu biểu về các loại tìm kiếm:

  • Boyer-Moore: hoạt động bằng cách phân tích trước mẫu và so sánh từ phải sang trái. Nếu xảy ra sự không phù hợp, phân tích ban đầu được sử dụng để xác định mẫu có thể được dịch chuyển đến đâu khi văn bản được tìm kiếm. Điều này đặc biệt tốt cho các mẫu tìm kiếm dài. Cụ thể, nó có thể là tuyến tính phụ, vì bạn không cần phải đọc từng ký tự của văn bản.

  • Knuth-Morris-Pratt: cũng phân tích trước mẫu, nhưng cố gắng sử dụng lại bất cứ thứ gì đã khớp trong phần ban đầu của mẫu để tránh phải làm lại mẫu đó. Điều này có thể hoạt động khá tốt, nếu bảng chữ cái của bạn nhỏ (ví dụ: cơ sở DNA), vì bạn có cơ hội cao hơn rằng các mẫu tìm kiếm của bạn có chứa các mẫu con có thể sử dụng lại.

  • Aho-Corasick: Cần rất nhiều tiền xử lý, nhưng làm như vậy đối với một số mẫu. Nếu bạn biết bạn sẽ tìm kiếm các mẫu tìm kiếm giống nhau nhiều lần, thì cái này tốt hơn nhiều so với cái kia, bởi vì bạn chỉ cần phân tích các mẫu một lần, không phải một lần cho mỗi tìm kiếm.

Do đó, như thường lệ trong CS, không có câu trả lời chắc chắn cho tổng thể tốt nhất . Nó là một vấn đề của việc lựa chọn công cụ phù hợp cho công việc trong tay.

Một lưu ý khác về lý do trường hợp xấu nhất của bạn: Hãy xem xét các loại tìm kiếm cần thiết để tạo ra trường hợp xấu nhất đó và suy nghĩ kỹ xem liệu những điều này có thực sự phù hợp trong trường hợp của bạn không. Ví dụ: O(mn)độ phức tạp trong trường hợp xấu nhất của thuật toán Boyer-Moore bắt nguồn từ một mẫu tìm kiếm và một văn bản chỉ sử dụng một ký tự (như tìm aaatrong aaaaaaaaaaaaaaaaaaaaa) - bạn có thực sự cần phải nhanh chóng tìm kiếm như vậy không?


Tôi có toàn bộ bảng chữ cái tiếng Anh để sử dụng và tôi đã cập nhật Câu hỏi, xin lỗi vì đã không bắt đầu với điều này tại cầu xin.
vandamon taigi

Và vâng, tôi cần phải nhanh chóng ngay cả đối với các tìm kiếm như vậy
vandamon taigi

1

Mặc dù tôi hơi muộn để trả lời câu hỏi này, nhưng tôi nghĩ Z-Algorithmlà nhanh hơn nhiều so với bất kỳ đối tác của nó. Độ phức tạp trong trường hợp xấu nhất của nó là O (m + n) và nó không yêu cầu tiền xử lý mẫu / văn bản. Nó cũng rất dễ dàng để mã hóa so với các thuật toán khác.

Nó hoạt động theo cách sau.

Ví dụ, có một chuỗi S ='abaaba'. Chúng tôi phải tìm z(i)giá trị cho i=0 to len(S)-1. Trước khi đi vào giải thích, hãy để tôi đặt một số định nghĩa đầu tiên.

z(i)= không. các ký tự của tiền tố Sphù hợp với tiền tố của s(i).

s(i)= ithhậu tố của S.

Sau đây là các s(i)giá trị cho s = 'abaaba'.

s(0) = 'abaaba' = S
s(1) = 'baaba'
s(2) = 'aaba'
s(3) = 'aba'
s(4) = 'ba'
s(5) = 'a'

Các giá trị z tương ứng

z(0) = 6 = length(S)
z(1) = 0
z(2) = 1
z(3) = 3
z(4) = 0
z(5) = 1

Để hiểu chi tiết về thuật toán, hãy tham khảo các liên kết sau.

http://codeforces.com/blog/entry/3107

https://www.youtube.com/watch?v=MFK0WYeVEag

Bây giờ, phải mất O (N) để tìm tất cả các zgiá trị mà không cần bất kỳ chi phí xử lý trước nào. Bây giờ người ta sẽ tự hỏi làm thế nào bạn có thể sử dụng logic này để khớp với mẫu trong một chuỗi nhất định?

Hãy xem với một ví dụ. Mẫu (P) : aba, Văn bản (T) : aacbabcabaad.

Đặt cái này ở dạng P $ T. ( $- bất kỳ ký tự nào không xuất hiện trong cả mẫu hoặc văn bản. Tôi sẽ đi đến tầm quan trọng $trong một thời gian ngắn.)

P$T = = aba$aacbabcabaad

Chúng tôi biết len(P)= 3.

Tất cả giá trị z P$T

z(0) = 16 = len(P$T)
z(1) = 0
z(2) = 1
z(3) = 0
z(4) = 1
z(5) = 1
z(6) = 0
z(7) = 0
z(8) = 2
z(9) = 0
z(10) = 0
z(11) = 3
z(12) = 0
z(13) = 1
Z(14) = 1
Z(15) = 0

Bây giờ mà z(i)= len(P). Ans = 11.Vì vậy, mô hình của chúng tôi có mặt tại Ans-len(P)-1= 7. -1là dành cho $nhân vật.

Bây giờ tại sao $hoặc bất kỳ nhân vật đặc biệt như vậy là quan trọng. Hãy xem xét P = 'aaa'T = 'aaaaaaa'. Không có ký tự đặc biệt, tất cả z(i)sẽ có giá trị gia tăng. Người ta vẫn có thể tìm thấy vị trí của mẫu trong văn bản với các công thức dưới đây:

Điều kiện: z(i)> = len(P)và Vị trí : Ans-len(P). Nhưng điều kiện trong trường hợp này trở nên hơi khó hiểu và khó hiểu. Cá nhân tôi thích sử dụng các kỹ thuật nhân vật đặc biệt.


1
Bạn có thể giải thích nó ở đây? Có liên kết đến các trang web bên ngoài có thể được sử dụng để xây dựng, nhưng cốt lõi của một câu trả lời nên nằm trong chính câu trả lời thay vì phải theo một liên kết đến một trang web khác.

Thuật toán z về cơ bản giống như kmp. Tôi nghi ngờ nó nhanh hơn nhiều.
Thomas Ahle

2
Tôi đồng ý với @ThomasAhle. Điện toán z tiền xử lý. Đó là một lời giải thích tốt, mặc dù. Tôi đưa ra một O(n)cách để chuyển đổi từ tiền xử lý KMP sang tiền xử lý Z, do câu trả lời này. Tại đây
leewz

-1

Sử dụng bộ nhớ có thể định địa chỉ nội dung , được triển khai trong phần mềm dưới dạng địa chỉ ảo (trỏ chữ vào chữ cái).

Nó không cần thiết đối với thuật toán khớp chuỗi trung bình.

CAM có thể khớp một số lượng lớn các mẫu đồng thời, tối đa khoảng 128 mẫu chữ cái (nếu chúng là ASCII; nếu chúng chỉ là Unicode 64). Và đó là một cuộc gọi cho mỗi độ dài chữ cái trong chuỗi bạn muốn khớp và một lần đọc ngẫu nhiên từ bộ nhớ trên mỗi độ dài của chiều dài mẫu tối đa. Vì vậy, nếu bạn đang phân tích chuỗi 100.000 ký tự, với tối đa 90.000.000 mẫu (sẽ mất khoảng 128 GiB để lưu trữ số lượng mẫu lớn), sẽ mất 12.800.000 lượt đọc ngẫu nhiên từ RAM, do đó sẽ xảy ra trong 1ms.

Đây là cách hoạt động của địa chỉ ảo.

Nếu tôi bắt đầu với 256 địa chỉ bắt đầu, đại diện cho chữ cái đầu tiên, những chữ cái này trỏ đến 256 chữ cái tiếp theo. Nếu một mẫu không tồn tại, bạn không lưu trữ nó.

Vì vậy, nếu tôi liên tục liên kết các chữ cái với các chữ cái, thì giống như có 128 lát địa chỉ ảo trỏ đến địa chỉ ảo.

Điều đó sẽ hoạt động - nhưng để có được 900.000.000 mẫu phù hợp đồng thời, có một mẹo cuối cùng để thêm vào nó - và nó lợi dụng thực tế là bạn bắt đầu với rất nhiều lần sử dụng lại các bộ đệm thư này, nhưng sau đó nó sẽ phân tán. Nếu bạn liệt kê nội dung, thay vì phân bổ tất cả 256 ký tự, thì nó sẽ chậm đi rất ít và bạn sẽ tăng công suất gấp 100 lần, vì về cơ bản cuối cùng bạn chỉ nhận được 1 chữ cái được sử dụng trong mỗi bộ đệm con trỏ chữ cái (mà tôi đặt tên là ' bỏ trốn').

Nếu bạn muốn có một kết hợp chuỗi lân cận gần nhất thì bạn có nhiều chuỗi này chạy song song và bạn thu thập theo thứ bậc, vì vậy bạn lan truyền lỗi của mình ra không thiên vị. nếu bạn cố gắng làm hàng xóm gần nhất chỉ với một, thì bạn sẽ thiên về hướng bắt đầu của cây.


4
@MagnusRobertCarlWoot cho rằng bạn có cùng gavatar như roucer81, đó là sự trùng hợp về mặt thiên văn của sự va chạm mã băm hoặc bạn có cùng địa chỉ email. Nếu bạn cùng một cá nhân đứng sau cả hai tài khoản, bạn nên sử dụng biểu mẫu "liên hệ với chúng tôi" để hợp nhất chúng để bạn có được tín dụng phù hợp cho danh tiếng đạt được thông qua các câu trả lời cho câu trả lời này.
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.