La più piccola rotazione lessicografica di una stringa usando array di suffissi in O (n)


9

Citerò il problema da ACM 2003:

Considera una stringa di lunghezza n (1 <= n <= 100000). Determinare la sua rotazione lessicografica minima. Ad esempio, le rotazioni della stringa "alabala" sono:

alabala

labalaa

abalaal

balaala

alaalab

laalaba

aalabal

e il più piccolo tra loro è "aalabal".

Per quanto riguarda la soluzione - so che devo costruire un array di suffissi - e diciamo che posso farlo in O (n). La mia domanda è ancora, come posso trovare la rotazione più piccola in O (n)? (n = lunghezza di una stringa)

Sono molto interessato a questo problema e ancora in qualche modo non riesco a trovare la soluzione. Sono più interessato al concetto e a come risolvere il problema e non all'attuazione concreta.

Nota: rotazione minima significa nello stesso ordine di un dizionario inglese - "dwor" è prima di "word" perché d è prima di w.

EDIT: la costruzione dell'array di suffissi richiede O (N)

ULTIMA MODIFICA: penso di aver trovato una soluzione !!! E se avessi appena unito due stringhe? Quindi se la stringa è "alabala" la nuova stringa mi "alabalaalabala" e ora costruirò un array di suffissi (in O (2n) = O (n)) e otterrei il primo suffisso? Immagino che potrebbe essere giusto. Cosa ne pensi? Grazie!


Come si definisce "minimo"? Qual è la metrica utilizzata (forse è ovvio ma non sono un esperto)?
Giorgio

Grazie per la nota! Pensavo che la rotazione dovesse essere minima (offset minimo), non il risultato della rotazione rispetto all'ordine lessicografico.
Giorgio,

Mi manca ancora qualcosa: la costruzione e l'ordinamento dell'array di suffissi sono inclusi nella complessità? Immagino che ci voglia più di O (n) per costruire l'array e ordinarlo.
Giorgio

Penso che l'idea di ripetere due volte la stringa originale sia fantastica! Quindi è possibile creare l'array di suffissi in O (2n) = O (n). Ma non è necessario ordinarlo per trovare il minimo? Questo ha bisogno di più di O (n), giusto?
Giorgio,

@Giorgio bene, l'array di suffissi stesso contiene i suffissi già ordinati . E un'altra nota, forse leggermente offtopica: non dimenticare che l'ordinamento può essere fatto anche in o (n) con alcune ipotesi sugli oggetti ordinati (controlla ad esempio l'ordinamento radix)
Tomy,

Risposte:


5

Un semplice trucco per costruire tutte le rotazioni di una stringa di lunghezza N è concatenare la stringa con se stessa.

Quindi ogni sottostringa di lunghezza N di questa stringa di lunghezza 2N è una rotazione della stringa originale.

La localizzazione della sottostringa "lessicograficamente minima" viene quindi eseguita con la struttura ad albero O (N).


0

Sono abbastanza sicuro che le informazioni contenute in un array di suffissi non siano sufficienti per aiutarti ad arrivare a O (n), ma al massimo possono aiutarti ad O (n log n). Considera questa famiglia di suffissi:

a
aba
abacaba
abacabadabacaba
abacabadabacabaeabacabadabacaba
...

Costruisci il suffisso successivo prendendo il suffisso precedente (ad esempio aba), aggiungendo il carattere successivo non ancora utilizzato e quindi aggiungendo nuovamente il suffisso precedente (quindi aba -> aba c aba).

Ora considera queste stringhe (lo spazio viene aggiunto per enfasi, ma non fa parte della stringa):

ad abacaba
bd abacaba
cd abacaba

Per queste tre stringhe, l'inizio dell'array del suffisso sarà simile al seguente:

a
aba
abacaba
(other suffixes)

Sembra familiare? Queste stringhe ovviamente sono personalizzate per creare questo array di suffissi. Ora, a seconda della lettera iniziale (a, b o c), l'indice "corretto" (la soluzione al tuo problema) è il primo, il secondo o il terzo suffisso nell'elenco sopra.

La scelta della prima lettera influisce difficilmente sull'array di suffissi; in particolare, non influisce sull'ordine dei primi tre suffissi nella matrice di suffissi. Ciò significa che abbiamo n stringhe di registro per le quali l'array di suffissi è estremamente simile ma l'indice "corretto" è molto diverso.

Anche se non ho prove concrete, questo mi suggerisce fortemente che non hai altra scelta che confrontare le rotazioni corrispondenti a questi primi tre indici nella matrice per il loro ordinamento lessicografico, il che a sua volta significa che avrai bisogno di almeno O (n log n) tempo per questo (poiché il numero di primi caratteri alternativi - nel nostro caso 3 - è log n, e il confronto di due stringhe richiede O (n) tempo).

Ciò non esclude la possibilità di un algoritmo O (n). Ho semplicemente dubbi sul fatto che un array di suffissi ti aiuti a raggiungere questo tempo di esecuzione.


0

La rotazione più piccola è quella che inizia con parte del suffisso dell'array di suffissi. I suffissi sono ordinati lessicograficamente. Questo ti dà un grande balzo in avanti:

  • sai che una volta che ottieni tale k che la rotazione che inizia con il suffisso k è più piccola della rotazione che inizia con il suffisso k +1, hai finito (a partire dal primo);
  • puoi fare il confronto tra "la rotazione che inizia con il suffisso k è minore della rotazione che inizia con il suffisso k +1" in O (1) confrontando le lunghezze dei suffissi e, facoltativamente, confrontando un carattere con un altro.

EDIT: "un personaggio con un altro" potrebbe non essere sempre così, potrebbe essere più di un personaggio, ma nel complesso, non si esaminano più di n caratteri durante l'intero processo di ricerca, quindi è O (n).

Breve prova: Esamini i caratteri solo quando il suffisso k +1 è più lungo del suffisso k e ti fermi e hai trovato la soluzione se il suffisso k +1 è più corto del suffisso k (quindi sai che il suffisso k è quello che cercavi). Quindi esamini i caratteri solo mentre ti trovi in ​​una sequenza di suffissi crescente (in termini di lunghezza). Poiché si esaminano solo caratteri in eccesso, non è possibile esaminare più di n caratteri.

EDIT2: questo algoritmo si basa sul fatto che "se ci sono due suffissi vicini nell'array di suffissi e il precedente è più corto del successivo, il precedente è prefisso del successivo". Se questo non è vero, allora scusa.

EDIT3: No, non regge. "abaaa" ha la tabella dei suffissi "a", "aa", "aaa", "abaaa", "baaa". Ma forse questa linea di pensiero alla fine può portare alla soluzione, solo alcuni dettagli devono essere più sofisticati. La domanda principale è se è possibile in qualche modo fare la comparazione di cui sopra fatta esaminando meno caratteri, quindi è O (n) totalmente, che in qualche modo credo possa essere possibile. Non riesco proprio a capire come, ora.


0

Problema:

La sottostringa lessicograficamente meno circolare è il problema di trovare la rotazione di una stringa che possiede l'ordine lessicografico più basso di tutte queste rotazioni. Ad esempio, la rotazione lessicograficamente minima di "bbaaccaadd" sarebbe "aaccaaddbb".

Soluzione:

L'algoritmo AO (n) time è stato proposto da Jean Pierre Duval (1983).

Dati due indici ie j, l'algoritmo di Duval confronta i segmenti di stringa di lunghezza a j - ipartire da ie j(chiamato "duello" ). Se index + j - iè maggiore della lunghezza della stringa, il segmento viene formato avvolgendolo.

Ad esempio, considera s = "baabbaba", i = 5 e j = 7. Poiché j - i = 2, il primo segmento che inizia con i = 5 è "ab". Il secondo segmento che inizia con j = 7 è costruito avvolgendosi ed è anche "ab". Se le stringhe sono lessicograficamente uguali, come nell'esempio sopra, scegliamo quello che inizia da i come vincitore, ovvero i = 5.

Il processo sopra descritto si è ripetuto fino a quando non avremo un solo vincitore. Se la stringa di input ha una lunghezza dispari, l'ultimo carattere vince senza confronto nella prima iterazione.

Complessità temporale:

La prima iterazione confronta n stringhe ciascuna di lunghezza 1 (n / 2 confronti), la seconda iterazione può confrontare n / 2 stringhe di lunghezza 2 (n / 2 confronti) e così via, fino a quando l'i-iterazione confronta 2 stringhe di lunghezza n / 2 (n / 2 confronti). Poiché il numero di vincitori viene dimezzato ogni volta, l'altezza dell'albero di ricorsione è log (n), dandoci così un algoritmo O (n log (n)). Per n piccolo, questo è approssimativamente O (n).

Anche la complessità dello spazio è O (n), poiché nella prima iterazione, dobbiamo memorizzare n / 2 vincitori, seconda iterazione n / 4 vincitori e così via. (Wikipedia afferma che questo algoritmo utilizza uno spazio costante, non capisco come).

Ecco un'implementazione di Scala; sentiti libero di convertire il tuo linguaggio di programmazione preferito.

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

Non vedo niente di meglio di O (N²).

Se si dispone di un elenco di N numeri interi, è possibile scegliere il più piccolo nei confronti O (N).

Qui hai un elenco di N stringhe di dimensione N (costruendole non costa nulla, una stringa è completamente determinata dal suo indice iniziale). Puoi scegliere il più piccolo nei confronti di O (N). Ma ogni confronto è O (N) operazioni di base. Quindi la complessità è O (N²).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.