ปัญหา:
สตริงย่อยแบบวงกลมน้อยที่สุดของ Lexicographically เป็นปัญหาของการค้นหาการหมุนของสตริงที่มีคำสั่งพจนานุกรมที่ต่ำที่สุดของการหมุนดังกล่าวทั้งหมด ตัวอย่างเช่นการหมุนด้วยคำน้อยที่สุดของ "bbaaccaadd" จะเป็น "aaccaaddbb"
สารละลาย:
ขั้นตอนวิธีเวลา (n) ถูกเสนอโดย Jean Pierre Duval (1983)
กำหนดสองดัชนีi
และj
อัลกอริธึมของ Duval เปรียบเทียบส่วนของสตริงที่มีความยาวj - i
เริ่มต้นที่i
และj
(เรียกว่า"duel" ) หากindex + j - i
มากกว่าความยาวของสตริงส่วนจะเกิดขึ้นโดยการล้อมรอบ
ตัวอย่างเช่นพิจารณา s = "baabbaba", i = 5 และ j = 7 เนื่องจาก j - i = 2 ส่วนแรกที่เริ่มต้นที่ i = 5 คือ "ab" ส่วนที่สองเริ่มต้นที่ j = 7 ถูกสร้างขึ้นโดยล้อมรอบและยังเป็น "ab" หากสตริงมีความเหมือนกันของคำศัพท์เช่นในตัวอย่างข้างต้นเราจะเลือกสตริงที่เริ่มต้นที่ฉันเป็นผู้ชนะซึ่งก็คือ i = 5
กระบวนการข้างต้นซ้ำจนกว่าเราจะมีผู้ชนะเพียงคนเดียว หากสตริงอินพุตมีความยาวคี่อักขระตัวสุดท้ายจะชนะโดยไม่มีการเปรียบเทียบในการคำนวณซ้ำครั้งแรก
ความซับซ้อนของเวลา:
การทำซ้ำครั้งแรกเปรียบเทียบ n สายแต่ละความยาว 1 (n / 2 การเปรียบเทียบ), การทำซ้ำครั้งที่สองอาจเปรียบเทียบ n / 2 สายยาว 2 (n / 2 เปรียบเทียบ), และต่อไปเรื่อย ๆ จนกระทั่งการทำซ้ำ i-th เปรียบเทียบ 2 สายของ ความยาว n / 2 (การเปรียบเทียบ n / 2) เนื่องจากจำนวนผู้ชนะจะลดลงครึ่งหนึ่งในแต่ละครั้งความสูงของแผนภูมิการเรียกซ้ำคือ log (n) ดังนั้นจึงให้อัลกอริทึม O (n log (n)) กับเรา สำหรับ n ขนาดเล็กนี่คือประมาณ O (n)
ความซับซ้อนของพื้นที่ก็คือ O (n) เช่นกันเนื่องจากในการทำซ้ำครั้งแรกเราจะต้องเก็บผู้ชนะ n / 2, ผู้ชนะซ้ำที่สอง n / 4 และอื่น ๆ (Wikipedia อ้างว่าอัลกอริทึมนี้ใช้พื้นที่คงที่ฉันไม่เข้าใจวิธี)
นี่คือการดำเนินการตามสกาล่า; อย่าลังเลที่จะแปลงเป็นภาษาโปรแกรมที่คุณชื่นชอบ
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)
}