Pagini recente » Cod sursa (job #2708925) | Cod sursa (job #2990474) | Cod sursa (job #1319611) | Cod sursa (job #2744181) | Cod sursa (job #2962618)
#include <bits/stdc++.h>
using namespace std;
ifstream fin("adn.in");
ofstream fout("adn.out");
int n;
vector<string> seq;
// functia kmp care returneaza vectorul pi care contine lungimea maxima a prefixului care este si sufix
vector<int> kmp(string s, string t) {
string p = s + '#' + t;
vector<int> pi(p.size(), 0);
int j = 0;
for (int i = 1; i < p.size(); i++) {
while (j > 0 && p[i] != p[j])
j = pi[j - 1];
if (p[i] == p[j])
j++;
pi[i] = j;
}
return pi;
}
bool isSubstr(string s, string t) {
vector<int> pi = kmp(s, t);
for (int i = s.size() + 1; i < pi.size(); i++)
if (pi[i] == s.size())
return true;
return false;
}
int main() {
fin >> n;
seq.resize(n);
for (int i = 0; i < n; i++)
fin >> seq[i];
// stergem duplicatele
sort(seq.begin(), seq.end());
seq.erase(unique(seq.begin(), seq.end()), seq.end());
n = seq.size();
// stergem cuvintele care sunt substring ale altora
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (i != j && isSubstr(seq[i], seq[j])) {
seq.erase(seq.begin() + i);
i--;
n--;
break;
}
int n = seq.size();
// calculam costul de alaturare a 2 secvente folosind kmp
vector<vector<int>> cost(n, vector<int>(n, 0));
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (i != j) {
vector<int> pi = kmp(seq[i], seq[j]);
cost[i][j] = seq[j].size() - pi.back();
}
// folosim ciclul hamiltonian pentru a gasi o solutie de alaturare a secventelor astfel incat costul total sa fie minim
// in d retinem costul minim al unei solutii
// prev e un vector care retine nodul anterior si starea din lantul hamiltonian pentru a putea reconstrui solutia
vector<vector<int>> d(n, vector<int>((1 << n), INT_MAX));
vector<vector<pair<int, int>>> prev(n, vector<pair<int, int>>((1 << n)));
// d[nod][1<<nod] = 0 pentru ca costul de alaturare a unei secvente cu ea insasi este 0
for (int i = 0; i < n; i++)
d[i][1 << i] = 0;
// prev[i][1<<i] = {-1, 0} pentru ca nu exista nod anterior
for (int i = 0; i < n; i++)
prev[i][1 << i] = {-1, 0};
// parcurgem toate starile posibile
for (int state = 1; state < (1 << n); state++)
for (int i = 0; i < n; i++)
// daca nodul i este in starea curenta
if (state & (1 << i))
// parcurgem toate nodurile vecine
for (int j = 0; j < n; j++)
// daca nodul j nu este in starea curenta
if (!(state & (1 << j))) {
// noua stare este starea curenta cu nodul j adaugat
int new_state = state | (1 << j);
// daca costul de alaturare a nodului i cu nodul j este mai mic decat costul minim al unei solutii
if (d[i][state] + cost[i][j] < d[j][new_state]) {
// actualizam costul minim al unei solutii
d[j][new_state] = d[i][state] + cost[i][j];
// actualizam nodul anterior si starea din lantul hamiltonian
prev[j][new_state] = {i, state};
}
}
// gasim costul minim al unei solutii si nodul final
int min_cost = INT_MAX, node;
for (int i = 0; i < n; i++)
if (d[i][(1 << n) - 1] < min_cost) {
min_cost = d[i][(1 << n) - 1];
node = i;
}
// reconstruim solutia
vector<int> sol;
int state = (1 << n) - 1;
while (state) {
sol.push_back(node);
int new_node = prev[node][state].first;
state = prev[node][state].second;
node = new_node;
}
reverse(sol.begin(), sol.end());
// afisam solutia care este reprezentata de cea mai scurta secventa de adn
// care contine toate secventele date
string ans = seq[sol[0]];
for (int i = 1; i < sol.size(); i++) {
int len = seq[sol[i]].size() - cost[sol[i - 1]][sol[i]];
ans += seq[sol[i]].substr(len);
}
fout << ans << '\n';
return 0;
}