Cod sursa(job #873023)

Utilizator vendettaSalajan Razvan vendetta Data 6 februarie 2013 20:18:16
Problema ADN Scor 30
Compilator cpp Status done
Runda Arhiva de probleme Marime 6.16 kb
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <set>
#include <queue>
#include <deque>

using namespace std;

ifstream f("adn.in");
ofstream g("adn.out");

#define nmax 19
#define inf (1<<30)
#define ll long long
#define Smax 30005

int n, urm[Smax];
string v[nmax];
bool viz[nmax];
int v2[nmax], mat[nmax][nmax];
int dp[1<<(nmax-1)+1][nmax];
string Rez; int Poz;

void citeste(){
    f >> n; f.get();
    string S;
    for(int i=1; i<=n; ++i){
        S.clear(); getline(f, S);
        v[i] = S;
        //cout << S.size() << "\n";
        //cout << v[i] << "\n";
    }
}

void scrie(string s){
    for(int i=1; i<s.size(); ++i) cout << s[i];
    cout << "\n";
}

void prefix(int poz){
    string S = v[poz];
    S += '#'; for(int i=S.size(); i>=1; --i) S[i] = S[i-1]; S += '#';
    for(int i=0; i<nmax; ++i) urm[i] = 0;
    //scrie(S);
    urm[1] = 0;
    for(int i=2, q=0; i<S.size(); ++i){
        while(q>0 && S[q+1] != S[i]) q = urm[q];
        if (S[q+1] == S[i]) ++q;
        urm[i] = q;
    }
}

inline int kmp(int st, int dr){
    string S = v[st]; string S2 = v[dr];
    S += '#';for(int i=S.size(); i>=1; --i) S[i] = S[i-1]; S += '#';
    S2 += '#';for(int i=S2.size(); i>=1; --i) S2[i] = S2[i-1]; S2 += '#';
    //scrie(S); scrie(S2);
    for(int i=1, q=0; i<S2.size(); ++i){
        while( q>0 && S[q+1] != S2[i]) q = urm[q];
        if (S[q+1] == S2[i]) ++q;
        if (q+2 == S.size() ) return 1;
    }
    return 0;
}

void elimina(){
    // elimin cuvintele mancate complet de alte cuvinte
    for(int i=1; i<=n; ++i){
        if (viz[i] == 1) continue;// viz[i] = 1 -> cuvantul i e eliminat
        prefix(i);
        for(int j=1; j<=n; ++j){
            if (i == j || viz[j] == 1 && v[i].size() < v[j].size()) continue;
            // acum vreau sa vad daca cuvantul j il mananca pe cuvantul i; adica daca i apare in j ca si subsecventa;
            if ( kmp(i, j) == 1 ){
                viz[i] = 1; break;
            }
        }
    }
    for(int i=1; i<=n; ++i){
        if (viz[i] == 1) continue;
        v2[ ++v2[0] ] = i;
    }
    n = v2[0];
}
/*
void bagaMat(){
    for(int i=1; i<=n; ++i){
        for(int j=1; j<=n; ++j){// j apare dupa i
            // deci imi trebuie cel mai mare prefix a lui j care e sufxi a lui i
            if (i == j) continue;

        }
    }

    for(int i=1; i<=n; ++i){
        for(int j=1; j<=n; ++j){
            cout << mat[i][j] << " " ;
        }
        cout << "\n";
    }


}
*/

inline bool check(string S, int poz, string S2){
    //cout << S << " " << S2 << "\n";
    int i=poz; int j=S2.size()-1;
    for(; i>=0 && j>=0; --i, --j){
        if (S[i] != S2[j]) return 0;
    }
    if (i>=0) return 0;
    return 1;
}

void brutMat(){
    for(int i=1; i<=n; ++i){
        for(int j=1; j<=n; ++j){
            int X = v[ v2[j] ].size();
            mat[i][j] = X;
            if (i == j) continue;
            string S = v[ v2[i] ]; string S2 = v[ v2[j] ];
            for(int j2=S2.size(); j2>=0; --j2){
                if ( check(S2, j2, S) == 1 ){
                    mat[i][j] = X-(j2+1);
                    break;
                }
            }
            //cout << i << " " << j << " " << mat[i][j] << "\n";
        }
    }
    //cout << "\n";
    for(int i=1; i<=n; ++i){
        for(int j=1; j<=n; ++j){
            int X = v[ v2[j] ].size();
            //cout <<  X - mat[i][j] << " " ;
        }
        //cout << "\n";
    }
}


void bagaDrum(int conf, int poz){
    // deci sunt in (conf,poz) am nevoie de un conf2 cu un bit de 1 mai putin a. i. dp[conf2] [poz2] + mat == dp[conf2][poz2];
    //cout << conf << " " << poz << "\n";
    if (conf == 0) for(int i=0; i<v[ v2[poz] ].size(); ++i) Rez += v[ v2[poz] ][i];

    for(int i=0; i<n; ++i){
        if (i == poz-1) continue;
        int oldConf = conf+(1<<(poz-1));
        if (dp[conf][i+1] + mat[i+1][poz] == dp[oldConf][poz]){
            //Rez.clear();
            //for(int j=v[ v2[poz] ].size()-mat[i+1][poz]; j<=v[ v2[poz] ].size()-1; ++j  ) Rez += v[ v2[poz] ][j];
            //cout << Rez << "\n";
            //Poz = i+1;
            bagaDrum(conf - (1<<i), i+1);
            for(int j=v[ v2[poz] ].size()-mat[i+1][poz]; j<=v[ v2[poz] ].size()-1; ++j  ) Rez += v[ v2[poz] ][j];
        }
    }
}

void rezolva(){
    // fac un dp[conf][j] = lungimea minima daca am cuvintele corespunzatoare bitilor de 1 din conf iar ultimul cuvant e al j-lea
    // problema care apare e urmatoarea eu trebuie sa fac cumva a.i. cand sunt in (conf,j) si vreau sa continui pe (conf2,k) cuvantul
    // j sa nu il "manance" definitiv pe "k"; asa ca primadata elimin cuvintele care sunt incluse complet in alte cuvinte
    // iar apoi mai bag o preprocesare (o sa am nevoide de ea in dinamica) gen mat[i][j] = cel mai mare prefix din j care e sufix a lui i
    // ordinea ar fi ca il pun pe i iar apoi pe j;
    elimina();
    //bagaMat();
    brutMat();
    //acum pot baga dinamica
    int lim = (1<<n);
    for(int conf=0; conf<lim; ++conf){
        for(int j=0; j<=n; ++j) dp[conf][j] = inf;
    }
    for(int j=0; j<n; ++j) dp[1<<j][j+1] = (v[ v2[j+1] ].size());
    //, cout << v[ v2[j+1] ].size() << "\n";

    for(int conf=0; conf<lim; ++conf){
        for(int j=0; j<n; ++j){
            if ( conf & (1<<j) ) {// se termina in j+1;
                int precConf = conf - (1<<j);
                for(int k=0; k<n; ++k){
                    if ( precConf & (1<<k) ){// se termina in k+1
                        int X = v[ v2[j+1] ].size();
                        dp[conf][j+1] = min(dp[conf][j+1], dp[precConf][k+1] + (mat[k+1][j+1]) );
                        //cout << dp[precConf][k+1] << " " << precConf << " " << k+1 << " " << mat[k+1][j+1] << "\n";
                    }
                }
            }
        }
    }
    int Min = inf; int poz = 0;
    for(int j=1; j<=n; ++j){
        if (Min > dp[lim-1][j]){
            Min = dp[lim-1][j]; poz = j;
        }
    }
    bagaDrum(lim-1-(1<<(poz-1)), poz);
    g << Rez << "\n";// << Min << "\n";

}

int main(){
    citeste();
    rezolva();

    f.close();
    g.close();

    return 0;
}