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 i
ve j
, Duval algoritması uzunluğu dize segmentleri karşılaştırır j - i
de başlangıç i
ve j
(a olarak adlandırılan "düello" ). Eğer index + j - i
dize 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)
}