Xoay vòng từ vựng nhỏ nhất của một chuỗi bằng cách sử dụng mảng hậu tố trong O (n)


9

Tôi sẽ trích dẫn vấn đề từ ACM 2003:

Xét một chuỗi có độ dài n (1 <= n <= 100000). Xác định vòng quay từ vựng tối thiểu của nó. Ví dụ: các phép quay của chuỗi Hồi giáo alabala 'là:

alabala

labalaa

bàn thờ

balaala

alaalab

laalaba

aalabal

và nhỏ nhất trong số đó là Hồi giáo aalabal.

Về giải pháp - tôi biết tôi cần xây dựng một mảng hậu tố - và giả sử tôi có thể làm điều đó trong O (n). Câu hỏi của tôi vẫn là, làm thế nào tôi có thể tìm thấy vòng quay nhỏ nhất trong O (n)? (n = chiều dài của một chuỗi)

Tôi rất quan tâm đến vấn đề này và bằng cách nào đó tôi vẫn không nhận được giải pháp. Tôi quan tâm nhiều hơn đến khái niệm và cách giải quyết vấn đề chứ không phải trong việc triển khai cụ thể.

Lưu ý: xoay tối thiểu có nghĩa là theo thứ tự như trong từ điển tiếng Anh - "dwor" đứng trước "word" vì d nằm trước w.

EDIT: xây dựng mảng hậu tố mất O (N)

EDIT LAST: Tôi nghĩ rằng tôi đã tìm thấy một giải pháp !!! Nếu tôi chỉ hợp nhất hai chuỗi thì sao? Vì vậy, nếu chuỗi là "alabala" thì chuỗi mới sẽ cho tôi "alabalaalabala" và bây giờ tôi chỉ cần xây dựng một mảng hậu tố của điều này (trong O (2n) = O (n)) và có hậu tố đầu tiên? Tôi đoán điều này có thể đúng. Bạn nghĩ sao? Cảm ơn bạn!


Làm thế nào để bạn xác định "tối thiểu"? Số liệu được sử dụng là gì (có thể rõ ràng nhưng tôi không phải là chuyên gia)?
Giorgio

Cảm ơn bạn đã lưu ý! Tôi nghĩ rằng vòng quay phải là tối thiểu (bù tối thiểu), không phải là kết quả của thứ tự từ điển wrt wrt.
Giorgio

Tôi vẫn còn thiếu một cái gì đó: việc xây dựng và sắp xếp các mảng hậu tố có bao gồm sự phức tạp không? Tôi tưởng tượng phải mất nhiều hơn O (n) để xây dựng mảng sắp xếp nó.
Giorgio

Tôi nghĩ rằng ý tưởng lặp lại chuỗi gốc hai lần là tuyệt vời! Sau đó, bạn có thể xây dựng mảng hậu tố trong O (2n) = O (n). Nhưng bạn không cần phải sắp xếp nó để tìm mức tối thiểu? Điều này cần nhiều hơn O (n), phải không?
Giorgio

@Giorgio tốt, mảng hậu tố giữ các hậu tố đã được sắp xếp . Và một lưu ý khác, có thể hơi khác thường - đừng quên rằng việc sắp xếp có thể được thực hiện ngay cả trong o (n) với một số giả định cho các đối tượng được sắp xếp (ví dụ: kiểm tra loại cơ số)
Tomy

Câu trả lời:


5

Một mẹo đơn giản để xây dựng tất cả các phép quay của một chuỗi có độ dài N là nối chuỗi đó với chính nó.

Sau đó, mỗi chuỗi con có độ dài N của chuỗi có độ dài 2N này là một vòng quay của chuỗi gốc.

Sau đó, việc định vị chuỗi con "tối thiểu từ vựng" được thực hiện với cấu trúc cây O (N) của bạn.


0

Tôi khá chắc chắn rằng thông tin chứa trong một mảng hậu tố là không đủ để giúp bạn đến O (n), nhưng nhiều nhất có thể giúp bạn đến O (n log n). Hãy xem xét họ hậu tố này:

a
aba
abacaba
abacabadabacaba
abacabadabacabaeabacabadabacaba
...

Bạn xây dựng hậu tố tiếp theo bằng cách lấy hậu tố trước (nói aba), thêm ký tự tiếp theo chưa được sử dụng và sau đó thêm hậu tố trước đó một lần nữa (vì vậy aba -> aba c aba).

Bây giờ hãy xem xét các chuỗi này (không gian được thêm vào để nhấn mạnh, nhưng không phải là một phần của chuỗi):

ad abacaba
bd abacaba
cd abacaba

Đối với ba chuỗi này, bắt đầu của mảng hậu tố sẽ như thế này:

a
aba
abacaba
(other suffixes)

Trông quen quen? Các chuỗi này tất nhiên được thiết kế để tạo ra mảng hậu tố này. Bây giờ, tùy thuộc vào chữ cái bắt đầu (a, b hoặc c), chỉ số 'chính xác' (giải pháp cho vấn đề của bạn) là hậu tố thứ nhất, thứ hai hoặc thứ ba trong danh sách trên.

Sự lựa chọn của chữ cái đầu tiên hầu như không ảnh hưởng đến mảng hậu tố; đặc biệt, nó không ảnh hưởng đến thứ tự của ba hậu tố đầu tiên trong mảng hậu tố. Điều này có nghĩa là chúng ta có các chuỗi log n mà mảng hậu tố cực kỳ giống nhau nhưng chỉ số 'chính xác' rất khác nhau.

Mặc dù tôi không có bằng chứng cứng, nhưng điều này gợi ý cho tôi rằng bạn không có lựa chọn nào khác ngoài việc so sánh các phép quay tương ứng với ba chỉ số đầu tiên trong mảng để sắp xếp thứ tự từ điển của chúng, điều đó có nghĩa là bạn sẽ cần ít nhất O (n log n) thời gian cho việc này (vì số lượng ký tự đầu tiên thay thế - trong trường hợp 3 của chúng tôi - là log n và việc so sánh hai chuỗi mất thời gian O (n)).

Điều này không loại trừ khả năng của thuật toán O (n). Tôi chỉ nghi ngờ rằng một mảng hậu tố giúp bạn đạt được thời gian chạy này.


0

Vòng xoay nhỏ nhất là vòng quay bắt đầu bằng một số hậu tố từ mảng hậu tố. Suffixes được sắp xếp theo từ vựng. Điều này mang đến cho bạn một bước khởi đầu lớn:

  • bạn biết rằng một khi bạn nhận được k như vậy thì vòng quay bắt đầu bằng hậu tố k nhỏ hơn vòng quay bắt đầu bằng hậu tố k +1, bạn đã hoàn thành (bắt đầu từ lần đầu tiên);
  • bạn có thể thực hiện so sánh "xoay bắt đầu bằng hậu tố k nhỏ hơn xoay bắt đầu bằng hậu tố k +1" trong O (1) bằng cách so sánh độ dài của hậu tố và tùy ý, so sánh một ký tự với một ký tự khác.

EDIT: "một ký tự với một ký tự khác" có thể không phải lúc nào cũng vậy, nó có thể nhiều hơn một ký tự, nhưng nhìn chung, bạn không kiểm tra nhiều hơn n ký tự trong toàn bộ quá trình tìm kiếm, vì vậy đó là O (n).

Bằng chứng ngắn: Bạn chỉ kiểm tra các ký tự khi hậu tố k +1 dài hơn hậu tố k và bạn dừng lại và tìm giải pháp của mình nếu hậu tố k +1 ngắn hơn hậu tố k (khi đó bạn biết hậu tố k là hậu tố bạn tìm kiếm). Vì vậy, bạn chỉ kiểm tra các ký tự trong khi bạn đang ở trong chuỗi các hậu tố tăng dần (theo chiều dài). Vì bạn chỉ kiểm tra các ký tự thừa, bạn không thể kiểm tra nhiều hơn n ký tự.

EDIT2: Thuật toán này dựa trên thực tế là "nếu có hai hậu tố lân cận trong mảng hậu tố và trước đó ngắn hơn sau, thì trước đó là tiền tố của sau". Nếu điều này không đúng, thì xin lỗi.

EDIT3: Không, nó không giữ. "abaaa" có bảng hậu tố "a", "aa", "aaa", "abaaa", "baaa". Nhưng có lẽ dòng suy nghĩ này cuối cùng có thể dẫn đến giải pháp, chỉ cần một số chi tiết nữa phải tinh vi hơn. Câu hỏi chính là liệu bằng cách nào đó có thể thực hiện việc so sánh đã nói ở trên bằng cách kiểm tra ít ký tự hơn, vì vậy đó hoàn toàn là O (n), điều mà tôi tin rằng bằng cách nào đó có thể có thể. Bây giờ tôi không thể nói như thế nào.


0

Vấn đề:

Chuỗi con tối thiểu theo vòng tròn là vấn đề tìm vòng quay của chuỗi sở hữu thứ tự từ điển thấp nhất trong tất cả các phép quay như vậy. Ví dụ: vòng quay tối thiểu theo từ vựng của "bbaaccaadd" sẽ là "aaccaaddbb".

Giải pháp:

Thuật toán thời gian AO (n) được đề xuất bởi Jean Pierre Duval (1983).

Đưa ra hai chỉ số ij, thuật toán của Duval so sánh các đoạn chuỗi có độ dài j - ibắt đầu từ ij(được gọi là "đấu tay đôi" ). Nếu index + j - ilớn hơn độ dài của chuỗi, phân đoạn được hình thành bằng cách quấn quanh.

Ví dụ: xem xét s = "baabbaba", i = 5 và j = 7. Vì j - i = 2, đoạn đầu tiên bắt đầu từ i = 5 là "ab". Đoạn thứ hai bắt đầu từ j = 7 được xây dựng bằng cách quấn quanh và cũng là "ab". Nếu các chuỗi bằng nhau về mặt từ vựng, như trong ví dụ trên, chúng tôi chọn chuỗi bắt đầu tại i là người chiến thắng, đó là i = 5.

Quá trình trên lặp đi lặp lại cho đến khi chúng tôi có một người chiến thắng duy nhất. Nếu chuỗi đầu vào có độ dài lẻ, ký tự cuối cùng sẽ thắng mà không cần so sánh trong lần lặp đầu tiên.

Thời gian phức tạp:

Lặp lại đầu tiên so sánh n chuỗi mỗi độ dài 1 (n / 2 so sánh), lần lặp thứ hai có thể so sánh n / 2 chuỗi có độ dài 2 (so sánh n / 2), và cứ thế, cho đến khi lần lặp thứ i so sánh 2 chuỗi chiều dài n / 2 (n / 2 so sánh). Vì số lượng người chiến thắng được giảm một nửa mỗi lần, chiều cao của cây đệ quy là log (n), do đó cung cấp cho chúng tôi thuật toán O (n log (n)). Đối với n nhỏ, đây là khoảng O (n).

Độ phức tạp của không gian cũng là O (n), vì trong lần lặp đầu tiên, chúng ta phải lưu trữ n / 2 người chiến thắng, người chiến thắng lần thứ hai n / 4, v.v. (Wikipedia tuyên bố thuật toán này sử dụng không gian không đổi, tôi không hiểu làm thế nào).

Đây là một triển khai Scala; cảm thấy tự do để chuyển đổi sang ngôn ngữ lập trình yêu thích của bạn.

def lexicographicallyMinRotation(s: String): String = {
 @tailrec
 def duel(winners: Seq[Int]): String = {
   if (winners.size == 1) s"${s.slice(winners.head, s.length)}${s.take(winners.head)}"
   else {
     val newWinners: Seq[Int] = winners
       .sliding(2, 2)
       .map {
         case Seq(x, y) =>
           val range = y - x
           Seq(x, y)
             .map { i =>
               val segment = if (s.isDefinedAt(i + range - 1)) s.slice(i, i + range)
               else s"${s.slice(i, s.length)}${s.take(s.length - i)}"
               (i, segment)
             }
             .reduce((a, b) => if (a._2 <= b._2) a else b)
             ._1
         case xs => xs.head
       }
       .toSeq
     duel(newWinners)
   }
 }

 duel(s.indices)
}

-1

Tôi không thấy gì tốt hơn O (N²).

Nếu bạn có một danh sách các số nguyên N, bạn có thể chọn số nhỏ nhất trong các so sánh O (N).

Ở đây bạn có một danh sách các chuỗi N có kích thước N (xây dựng chúng không tốn kém gì, một chuỗi được xác định đầy đủ bởi chỉ số bắt đầu của nó). Bạn có thể chọn nhỏ nhất trong so sánh O (N). Nhưng mỗi so sánh là O (N) hoạt động cơ bản. Vậy độ phức tạp là O (N²).

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.