Fattorizzazione delle parole in tempo


12

Dato due stringhe , scriviamo per la loro concatenazione. Data una stringa e intero , scriviamo per la concatenazione di copie di . Ora data una stringa, possiamo usare questa notazione per 'comprimerla', cioè può essere scritto come . Chiamiamo il peso di una compressione il numero di caratteri che vi appaiono, quindi il peso di è due e il peso di (una compressione di ) è tre (separatoS1,S2S1S2Sk1(S)k=SSSkSAABAAB((A)2B)2 ( ( A ) 2 B 2 ) ( A B ) 2 A A B A B A A((A)2B2)(AB)2AABABAA vengono conteggiati separatamente).

Consideriamo ora il problema di calcolare la compressione "più leggera" di una determinata stringa con . Dopo alcune riflessioni esiste un ovvio approccio di programmazione dinamica che funziona in o seconda dell'approccio esatto.S|S|=nO(n3logn)O(n3)

Tuttavia, mi è stato detto che questo problema può essere risolto in tempo, anche se non riesco a trovare alcuna fonte su come farlo. In particolare, questo problema è stato riscontrato in un recente concorso di programmazione (problema K qui , ultime due pagine). Durante l'analisi è stato presentato un algoritmo e alla fine è stato menzionato il limite pseudo quadratico ( qui al segno dei quattro minuti). Purtroppo il presentatore ha fatto riferimento solo a "una parola complicata di lemma combinatoria", quindi ora sono venuto qui per chiedere la soluzione :-)O(n2logn)O ( n 3 log n )O(n3logn)


Solo una proprietà casuale: se per una stringa abbiamo , allora deve essere anche [ Ho corretto un errore qui], con che ha lunghezza (che non può essere più lungo di o ). Non sono sicuro di quanto sia utile. Se hai già scoperto che e sai che contiene almeno 2 caratteri distinti, e ora stai cercando una più corta tale che , allora devi solo provare i prefissi di hanno lunghezza che divide.SS=Xa=YbS=Z|S|/gcd(|X|,|Y|)Zgcd(|X|,|Y|)XYS=XaSYS=YbYX|X|
j_random_hacker,

Il problema è che anche dopo aver ridotto tutte le possibili , è comunque necessario aggregare la risposta di un DP cubo su sottosegmenti (ovvero ), quindi c'è ancora del lavoro da fare dopo ...XaDP[l,r]=minkDP[l,k]+DP[k+1,r]
Timon Knigge,

Capisco cosa intendi. Penso che tu abbia bisogno di una sorta di relazione di dominanza che elimini alcuni valori di dalla necessità di essere testati, ma non sono stato in grado di pensarne uno. In particolare, ho considerato quanto segue: Supponiamo che abbia una fattorizzazione ottimale con ; è possibile che esista una soluzione ottimale in cui è fattorizzato come con ? Sfortunatamente la risposta è sì: per , ha una fattorizzazione ottimale , ma la fattorizzazione ottimale unica per è .kS[1..i]S[1..i]=XYkk>1SXYjZj<kS=ABABCABCS[1..4](AB)2SAB(ABC)2
j_random_hacker,

Risposte:


1

Se non ti sto fraintendendo, penso che la fattorizzazione del costo minimo possa essere calcolata nel tempo come segue.O(n2)

Per ogni indice i, calcoleremo un gruppo di valori per come segue. Sia il numero intero più piccolo in modo che esista un numero intero soddisfiPer questo particolare , sia la più grande con questa proprietà. Se tale esiste, impostare modo da sapere che ci sono zero valori per questo indice.(pi,ri)=1,2,pi11r2

S[irpi1+1,ipi1]=S[i(r1)pi1+1,i].
pi1ri1rpiLi=0(pi,ri)

Sia il numero intero più piccolo strettamente più grande di soddisfacente, similmente, per alcuni . Come prima, prendi per essere il massimo dopo aver corretto . In generale è il numero più piccolo di questo numero strettamente più grande di . Se non esiste tale , allora .pi2(ri11)pi1

S[iri2pi2+1,ipi2]=S[i(ri21)pi2+1,i]
ri22ri2pi2pi(ri11)pi1piLi=1

Si noti che per ogni indice i, abbiamo causa dei valori che aumentano geometricamente con . (se esiste , non è solo strettamente più grande di ma più grande di quello di almeno Ciò determina l'aumento geometrico. )Li=O(log(i+1))pipi+1(ri1)pipi/2

Supponiamo ora che tutti i valori vengano dati. Il costo minimo è dato dalla ricorrenza con la consapevolezza che per abbiamo impostato . La tabella può essere compilata in tempo.(pi,ri)

dp(i,j)=min{dp(i,j1)+1,min(dp(i,jrjpj)+dp(jrjpj+1,jpj))}
i>jdp(i,j)=+O(n2+njLj)

Abbiamo già osservato sopra che limitando il termine termine per termine. Ma in realtà se guardiamo l'intera somma, possiamo dimostrare qualcosa di più nitido.jLj=O(jlog(j+1))=Θ(nlogn)

Considera l'albero dei suffissi del rovescio di (cioè l'albero dei prefissi di S). ogni contributo alla somma su un bordo di modo che ogni bordo venga addebitato al massimo una volta. Carica ogni sul bordo che emana da e va verso . Qui è la foglia dell'albero del prefisso corrispondente a e nca indica l'antenato comune più vicino.T(S)SiLiT(S)pijnca(v(i),v(ipij))v(ipij)v(i)S[1..i]

Ciò mostra che . I valori possono essere calcolati nel tempo da un attraversamento dell'albero del suffisso, ma lascerò i dettagli a una modifica successiva se qualcuno è interessato.O(iLi)=O(n)(pij,rij)O(n+iLi)

Fammi sapere se questo ha senso.


-1

C'è la tua stringa iniziale S di lunghezza n. Ecco lo pseudo-codice del metodo.

next_end_bracket = n
for i in [0:n]: # main loop

    break if i >= length(S) # due to compression
    w = (next_end_bracket - i)# width to analyse

    for j in [w/2:0:-1]: # period loop, look for largest period first
        for r in [1:n]: # number of repetition loop
            if i+j*(r+1) > w:
                break r loop

            for k in [0:j-i]:
                # compare term to term and break at first difference
                if S[i+k] != S[i+r*j+k]:
                    break r loop

        if r > 1:
            # compress
            replace S[i:i+j*(r+1)] with ( S[i:i+j] )^r
            # don't forget to record end bracket...
            # and reduce w for the i-run, carrying on the j-loop for eventual smaller periods. 
            w = j-i

Ho intenzionalmente fornito piccoli dettagli sulle "parentesi graffe" in quanto richiede molti passaggi per impilare e separare, il che renderebbe poco chiaro il metodo di base. L'idea è quella di testare un'ulteriore contrazione all'interno della prima. per esempio ABCBCABCBC => (ABCBC) ² => (A (BC) ²) ².

Quindi il punto principale è cercare prima periodi di grandi dimensioni. Nota che S [i] è il termine di S che salta qualsiasi "(", ")" o potenza.

  • i-loop è O (n)
  • j-loop è O (n)
  • r + k-loops è O (log (n)) poiché si ferma alla prima differenza

Questo è globalmente O (n²log (n)).


Non mi è chiaro che i loop r e k siano O (log n), anche separatamente. Cosa garantisce che venga rilevata una differenza dopo al massimo O (log n) iterazioni?
j_random_hacker il

Capisco correttamente che stai comprimendo avidamente? Poiché ciò non è corretto, si consideri, ad esempio, ABABCCCABCCC che è necessario considerare come AB (ABC ^ 3) ^ 2.
Timon Knigge,

Sì, hai perfettamente ragione, devo pensarci.
Optidad,
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.