Un'odissea con Fibonacci's String

E' da parecchio che sto cercando di risolvere questo problema ma senza successo. La mia idea è questa: Scorro la stringa e per ogni posizione cerco la piu lunga stringa di fibonacci che parte da qui. Per fare questo creo sul momento una stringa di fibonacci di lunghezza adeguata( "a") e la confronto con la stringa che sto verificando. Notare che una stringa di fibonacci è a sua volta composta da stringhe di fibonacci di lunghezza cresciente : 1,1,2,3,5,8 ecc..., proprio come i numeri di fibonacci : a  b  a  ab  aba  abaab  abaababa.

Io sfrutto cio andando a verificare ogni volta la parte che si aggiunge che deve essere uguale alla mia appena generata stringa "a". L'algoritmo è abbastanza complesso da spiegare e mi scuso per ciò. Spero che guardando il codice riusciate a capire meglio.

A me questo codice/algoritmo sembrerebbe corretto,  a massimo che abbia problemi di TME, ma in realtà mi totalizza solo 36 punti, prendendo WA negli altri test cases, cosa che non mi spiego...spero mi possiate illuminare, ci sto soffrendo sopra da troppo!



int N,sol=1,indI,indF;

string fib="a",S;

vi fibNum;


int main() {

    ifstream in; ofstream out;

    in.open("input.txt"); out.open("output.txt");

    

    in>>N>>S;


    fibNum.push_back(1); fibNum.push_back(2);  //Creo un vettore con tutti i numeri di fibonacci fino a una lunghezza appena superiore

    for(int i=2;i<30;i++)              // a quella massima della stringa

        fibNum.push_back(f[i-1]+f[i-2]);

        


    for(int i=0;i<N-1;i++){

        char t=S[i],t2=S[i+1]; //Ad ogni passo considero carattere corrente e quello successivo e                                                       //provo a fare partire

        //la stringa di fibonacci da qui

        if(t==t2)

            continue;

        

        string a="",b="";

        a += t; b += t2;

        

        int shift = 0, k = 0, end = i+1, solTemp = 2;

        

        while( (i + 2 + shift + fibNum[k]) <= S.length() && S.compare( i + 2 + shift, fibNum[k], a)==0){

            end = i + 2 + shift + fibNum[k];

            shift += fibNum[k++];

            a += b;

            b = a;                   

        }

        if(k>0) solTemp = 2+shift;      //shift+2 rappresenta la lunghezza perche a shift viene aggiunto sempre l'ultimo fib number e alla fine ne possedera uno in piu del dovuto

        

        if(solTemp > sol){

                sol = solTemp;

                indI = i;

                indF = i+solTemp-1;

        }         

       

    }

    

    out<<indI+1<<" "<<indF+1;

    

    in.close(); out.close();

    return 0;

}

//25 

Le stringhe di fibonacci hanno alcune proprietà.
La lunghezza della stringa di fibonacci F(N) è data dalla somma delle lunghezze di F(N-1) e F(N-2). Quindi la sequenza delle lunghezze altro non è che la classica sequenza di fibonacci, come già avevi detto tu.
Però occorre fare un'importante considerazione. Se in una sequenza di caratteri viene trovata una stringa di fibonacci di I=X+Y caratteri (dove X e Y sono le lunghezze delle stringhe che la compongono) e i successivi Y caratteri rappresentano una stringa di fibonacci che ha come caratteri gli stessi della prima stringa trovata, allora la stringa lunga I+Y caratteri rappresenta un'ulteriore stringa di fibonacci e questo processo si può ripetere per i prossimi I caratteri.
Iterando questo procedimento fino alla fine della stringa (controllando che due caratteri successivi siano diversi) avrai 100/100.
Spero di essermi spiegato bene

Si ti sei spiegato, ma è proprio quello che sembra di fare a me, adesso al massimo proverò di reimplementarlo.

Boh non so che dirti, ieri non ho avuto il tempo di leggere il tuo codice e quindi non ho fatto altro che copiare e incollare la descrizione dell’algoritmo che metto in ogni mia soluzione (fortunatamente). Ora lo guardo un attimo e vedo se trovo qualcosa che non va, anche se il mio codice è meno complesso del tuo

Grazie ma l’ho già rimplementato, semplificando abbastanza il mio codice(che in effetti era piuttosto contorto) ed ho ottenuto un bel 100/100 :slight_smile:

Comunque evidentemente mi perdo qualcosa, perchè il mio codice, andando a verificare per ogni carattere la piu lunga stringa di fibonacci che parte da li, ha un costo che non riesco a definire bene, però sicuramente maggiore di lineare. Non è neanche quadratico perchè non è possibile che da ogni carattere parta una stringa di fibonacci per questioni fisiche, però sicuramente maggiore di lineare, allora avendo un N che può essere 1 000 000, non capisco come il mio programma abbia un worst case di 0.04 s.

Non ti so dire con certezza, non sono molto pratico in queste cose. Anzi credo che in pochi qui si preoccupino più di tanto della complessità vera e propria di un algoritmo :slight_smile:

La tua soluzione dovrebbe essere quadratica: per ogni posizione (in tutto N) provi a vedere i successivi Z caratteri se formano una stringa di Fibonacci. Poiché Z è potenzialmente lungo a piacere, la soluzione è sostanzialmente quadratica.

Nella teoria della complessità computazionale non è rilevante il fatto che non controlli alcune posizioni (anche se ne saltassi la metà). Detto ciò, sicuramente la soluzione è particolarmente ottimizzata dal compilatore (compare ha affinità con strcmp che viene pesantemente ottimizzata da glibc).
La tua soluzione dovrebbe essere quadratica: per ogni posizione (in tutto N) provi a vedere i successivi Z caratteri se formano una stringa di Fibonacci. Poiché Z è potenzialmente lungo a piacere, la soluzione è sostanzialmente quadratica.

lucach

Per esattezza, quanto detto è sufficiente solo ad affermare che il tempo di esecuzione è O(n²). Però nota che anche un semplice ciclo for da 1 a n rispetta la definizione di O grande ed è a tutti gli effetti O(n²), o anche O(n³), etc.

Per esempio (ma non dico che sia così) non è da escludere che, per via di qualche proprietà dei numeri di Fibonacci, il tempo di esecuzione sia "anche" $O(n^{1.5})$.

Certo, non potra mai essere $O(n^{0.5})$, perché contraddiciamo il fatto che è Ω(n), dato che dobbiamo scorrere almeno una volta ogni elemento della sequenza.
Per esempio (ma non dico che sia così) non è da escludere che, per via di qualche proprietà dei numeri di Fibonacci, il tempo di esecuzione sia "anche" $O(n^{1.5})$.

wil93

Esattamente quello che intendevo, è sicuramente maggiore di lineare e tendenzialmente minore di quadratica per proprieta dei numeri di fibonacci, come upper bound possiamo porre O(n^2) ma in realtà non ci arriverà mai a questo valore, però mi fa comunque rimanere perplesso che una soluzione non lineare ottenga tempi cosi buoni
però mi fa comunque rimanere perplesso che una soluzione non lineare ottenga tempi cosi buoni

EmanueleRossi

Beh, per esempio una soluzione non lineare, ma che è O(n lg n), ha un tempo di esecuzione praticamente indistinguibile da una lineare nella maggior parte dei casi (o comunque per distinguerlo è necessario quantomeno usare i grader, altrimenti usando fast i/o si rischia di "sembrare" lineari e prendere punteggio pieno).

Probabilmente quella soluzione non sarà O(n lg n), ma non sappiamo "quanto è peggio" di una lineare.

Se vi va di discutere ulteriormente sull'analisi di quell'algoritmo, io ho chiesto [qui](http://cs.stackexchange.com/questions/28865/complexity-of-a-naive-algorithm-for-finding-the-longest-fibonacci-substring) :)

Interessante, forse questo era il forum di computer science che mi mancava, io continuavo a fare domande su stackoverflow senza che nessun mi cagasse e solo dopo ho capito che quello era più per la programmazione in se e meno per l’algoritmica…questo mi sa che è piu adatto, e dovrebbero anche essere collegati

Resuscito questo vecchio topic per dire che, alla fine, un professore di informatica ha risposto alla domanda sul tempo di esecuzione di questo algoritmo e ha detto che è effettivamente O(n \log n) :smile: