Pagini recente » Cod sursa (job #3146437) | Cod sursa (job #361051) | Cod sursa (job #616402) | Cod sursa (job #2917762) | Cod sursa (job #873023)
Cod sursa(job #873023)
#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;
}