Cod sursa(job #2962622)

Utilizator AlexPascu007Pascu Ionut Alexandru AlexPascu007 Data 8 ianuarie 2023 21:14:57
Problema ADN Scor 100
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 4.19 kb
#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 (auto it: pi)
        if (it == 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;
            }
    // 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) {
                cost[i][j] = kmp(seq[i], seq[j]).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_MIN));
    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[j][i] > d[j][new_state]) {
                            // actualizam costul minim al unei solutii
                            d[j][new_state] = d[i][state] + cost[j][i];
                            // 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_MIN, node = -1;
    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 (node != -1) {
        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 = cost[sol[i]][sol[i - 1]];
        ans += seq[sol[i]].substr(len);
    }
    fout << ans;

    return 0;
}