O (n) 'de sonek dizileri kullanarak bir dizenin en küçük sözlük dönüşü


9

Sorunu ACM 2003'ten alıntılayacağım:

N (1 <= n <= 100000) uzunluğunda bir dize düşünün. Minimum sözlükbilimsel dönüşünü belirleyin. Örneğin, “alabala” dizesinin dönüşleri:

alabala

labalaa

abalaal

balaala

alaalab

laalaba

aalabal

ve en küçüğü “aalabal”.

Çözüm gelince - bir sonek dizisi oluşturmak gerektiğini biliyorum - ve diyelim ki O (n) bunu yapabilirim. Benim sorum hala, O (n) 'deki en küçük dönüşü nasıl bulabilirim? (n = bir dizenin uzunluğu)

Bu sorunla çok ilgileniyorum ve yine de bir şekilde çözümü alamıyorum. Daha somut uygulama ile değil, kavram ve sorunun nasıl çözüleceği ile ilgileniyorum.

Not: minimum döndürme, İngilizce sözlüktekiyle aynı sırada anlamına gelir - "dwor", "word" den önce olduğu için d, w'den önce olduğu anlamına gelir.

EDIT: sonek dizi inşaatı O (N) alır

SON DÜZENLEME: Sanırım bir çözüm buldum !!! İki dizeyi birleştirirsem ne olur? Eğer dize "alabala" ise, yeni dize bana "alabalaalabala" olurdu ve şimdi ben sadece (O (2n) = O (n)) bir sonek dizisi inşa ediyorum ve ilk eki var mı? Sanırım bu doğru olabilir. Ne düşünüyorsun? Teşekkür ederim!


"Minimum" u nasıl tanımlıyorsunuz? Kullanılan metrik nedir (belki açıktır ama ben uzman değilim)?
Giorgio

Not için teşekkürler! Döndürmenin sözlükbilimsel sıralama sonucu değil, minimum (minimum ofset) olması gerektiğini düşündüm.
Giorgio

Hala bir şey eksik: İnşaat ve sonek dizisi sıralama karmaşıklığı dahil mi? Dizi oluşturmak ve sıralamak için O (n) daha fazla sürer hayal .
Giorgio

Orijinal dizeyi iki kez tekrarlama fikrinin harika olduğunu düşünüyorum! Sonra sonek dizisini O (2n) = O (n) 'de oluşturabilirsiniz. Ancak minimum değeri bulmak için sıralamanız gerekmez mi? Bunun O (n) 'den daha fazlasına ihtiyacı var, değil mi?
Giorgio

@Giorgio iyi, sonek dizisinin kendisi zaten sıralanmış yeterlilikleri tutar . Ve başka bir not, belki biraz oftopiktir - sıralamanın, nesnelerin bazı varsayımları ile o (n) 'de bile yapılabileceğini unutmayın (örneğin sayı tabanı sıralamasına bakın)
Tomy

Yanıtlar:


5

N uzunluğundaki bir dizenin tüm rotasyonlarını oluşturmak için basit bir hile, dizeyi kendisiyle birleştirmektir.

Daha sonra bu 2N uzunluktaki dizenin her N uzunluğu alt dizesi, orijinal dizenin dönüşüdür.

Daha sonra "sözlükbilimsel olarak en düşük" alt dizeyi bulmak O (N) ağaç yapınızla yapılır.


0

Bir sonek dizisinde bulunan bilgilerin O (n) 'ye ulaşmanıza yardımcı olmak için yeterli olmadığından eminim, ancak en çok O (n log n)' e yardımcı olabilir. Bu son ek ailesini düşünün:

a
aba
abacaba
abacabadabacaba
abacabadabacabaeabacabadabacaba
...

Bir önceki soneki (aba diyelim) alıp, henüz kullanılmayan bir sonraki karakteri ekleyerek ve ardından önceki soneki tekrar ekleyerek (son olarak aba -> aba c aba) bir sonraki soneki oluşturursunuz.

Şimdi bu dizeleri düşünün (boşluk vurgu için eklenir, ancak dizenin bir parçası değildir):

ad abacaba
bd abacaba
cd abacaba

Bu üç dize için, sonek dizisinin başlangıcı şöyle görünür:

a
aba
abacaba
(other suffixes)

Tanıdık geliyor? Bu dizeler elbette bu sonek dizisini oluşturacak şekilde uyarlanmıştır. Şimdi, başlangıç ​​harfine (a, b veya c) bağlı olarak, 'doğru' indeks (probleminizin çözümü) yukarıdaki listedeki ilk, ikinci veya üçüncü son ektir.

İlk harfin seçimi sonek dizisini pek etkilemez; özellikle, sonek dizisindeki ilk üç son ekin sırasını etkilemez. Bu, sonek dizisinin son derece benzer olduğu, ancak 'doğru' dizininin çok farklı olduğu log n dizelerimiz olduğu anlamına gelir.

Sert bir kanıtım olmamasına rağmen, bu, dizideki bu ilk üç endekse karşılık gelen rotasyonları sıralama için karşılaştırmaktan başka seçeneğiniz olmadığını güçlü bir şekilde gösteriyor, bu da en az O (n log n) bunun için zaman (alternatif ilk karakterlerin sayısı - bizim durumumuzda 3 - log n'dir ve iki dizenin karşılaştırılması O (n) zaman alır).

Bu, O (n) algoritması olasılığını ortadan kaldırmaz. Ben sadece bir sonek dizi bu çalışma süresi elde yardımcı olur şüphelerim var.


0

En küçük dönüş, sonek dizisinin sonekinin bir kısmı ile başlayan orandır. Son ekler sözlükbilimsel olarak sıralanmıştır. Bu size büyük bir hızlı başlangıç ​​sağlar:

  • böyle bir k elde edildikten sonra, k sonekiyle başlayan rotasyon , k +1 sonekiyle başlayan rotasyondan daha küçük olur , tamamlanırsınız (birinciden başlayarak);
  • Eğer "soneki ile başlayan dönme karşılaştırma yapmak yapabilir k eki ile başlayan dönme daha küçük olan K birbirine karakter ile bir karakter karşılaştırarak, isteğe bağlı olarak soneklerinin uzunlukları karşılaştırılarak, O (1) 'de +1".

DÜZENLEME: "bir karakter bir başka karakter" her zaman böyle olmayabilir, birden fazla karakter olabilir, ancak genel olarak, tüm arama işlemi boyunca n'den fazla karakter incelemezsiniz, bu nedenle O (n) olur.

Kısa kanıtı: eki zaman yalnızca karakterleri incelemek k 1 uzun eki daha k ve eki eğer size çözüm durdurmak ve bulunan k 1 sonek daha kısadır k (o bildiğiniz eki k için aranan biridir). Yani karakterleri yalnızca yükselen (uzunluk olarak) son ekler dizisi sırasında incelersiniz. Yalnızca fazla karakterleri incelediğiniz için n karakterden fazlasını inceleyemezsiniz.

EDIT2: Bu algoritma "sonek dizisinde iki komşu sonek varsa ve bir öncekinden bir öncekinden daha kısaysa, bir öncekinin diğerinin öneki olması" gerçeğine dayanır. Bu doğru değilse, üzgünüm.

EDIT3: Hayır, tutmuyor. "abaaa", "a", "aa", "aaa", "abaaa", "baaa" sonek tablolarına sahiptir. Ama belki de bu düşünce nihayetinde çözüme yol açabilir, sadece daha fazla ayrıntı daha sofistike olmalıdır. Birincil soru, daha az karakter inceleyerek bir şekilde yukarıda bahsedilen karşılaştırmanın yapılmasının mümkün olup olmadığıdır, bu yüzden bir şekilde mümkün olabileceğine inandığım tamamen O (n) 'dir. Nasıl olduğunu şimdi anlayamıyorum.


0

Sorun:

Sözlükbilimsel olarak en az dairesel alt dize, bu tür tüm dönüşlerin en düşük sözlükbilimsel sırasına sahip bir dizgenin dönüşünü bulma problemidir. Örneğin, "bbaaccaadd" nin sözlükbilimsel olarak minimum dönüşü "aaccaaddbb" olacaktır.

Çözüm:

AO (n) zaman algoritması Jean Pierre Duval (1983) tarafından önerilmiştir.

İki endeks dikkate alındığında ive j, Duval algoritması uzunluğu dize segmentleri karşılaştırır j - ide başlangıç ive j(a olarak adlandırılan "düello" ). Eğer index + j - idize uzunluğundan daha büyüktür, kademeli sararak oluşturulur.

Örneğin, s = "baabbaba", i = 5 ve j = 7'yi düşünün. J - i = 2 olduğundan, i = 5 ile başlayan ilk segment "ab" dir. J = 7'den başlayan ikinci segment, etrafına sarılarak yapılır ve ayrıca "ab" dir. Dizeler sözlükbilimsel olarak eşitse, yukarıdaki örnekte olduğu gibi, i ile başlayan diziyi kazanan olarak seçeriz, yani i = 5.

Yukarıdaki işlem tek bir kazanan olana kadar tekrarlandı. Giriş dizesi tek uzunlukta ise, son karakter ilk yinelemede karşılaştırma yapılmadan kazanır.

Zaman karmaşıklığı:

İlk yineleme, uzunluk 1 (n / 2 karşılaştırmaları) n dizesini karşılaştırır, ikinci yineleme, uzunluk 2 (n / 2 karşılaştırmaları) n / 2 dizelerini karşılaştırabilir ve bu şekilde, i-yineleme 2 dizeyi karşılaştırıncaya kadar uzunluk n / 2 (n / 2 karşılaştırmaları). Kazananların sayısı her seferinde yarıya indirildiğinden, özyineleme ağacının yüksekliği log (n) olur, böylece bize bir O (n log (n)) algoritması verir. Küçük n için bu yaklaşık O (n) 'dir.

Alan karmaşıklığı da O (n) 'dir, çünkü ilk yinelemede n / 2 kazananını, ikinci yineleme n / 4 kazananını vb. (Wikipedia bu algoritmanın sabit alan kullandığını iddia ediyor, nasıl olduğunu anlamıyorum).

İşte bir Scala uygulaması; En sevdiğiniz programlama diline dönüştürmekten çekinmeyin.

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

O (N²) 'den daha iyi bir şey görmüyorum.

N tam sayı listeniz varsa, O (N) karşılaştırmasında en küçük olanı seçebilirsiniz.

Burada N boyutunda N dizelerinin bir listesi var (bunları oluşturmak hiçbir maliyeti yoktur, bir dize başlangıç ​​diziniyle tamamen belirlenir). O (N) karşılaştırmasında en küçük olanı seçebilirsiniz. Ancak her karşılaştırma O (N) temel işlemidir. Yani karmaşıklık O (N²).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.