Cod sursa(job #3327555)

Utilizator BuzdiBuzdugan Rares Andrei Buzdi Data 4 decembrie 2025 15:17:27
Problema ADN Scor 100
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 3.79 kb
#include <bits/stdc++.h>
#define ll long long
#define cin fin
#define cout fout

using namespace std;

ifstream fin("adn.in");
ofstream fout("adn.out");

const int NMAX = 18;
const int INF = 1e9;
const int BASE = 37;
const int MOD = 1e9 + 9;
const int SMAX = 3e4;

int n, answer, ind, start;
int x[NMAX + 1];
string answer_s;
string a[NMAX + 1];
string b[NMAX + 1];
int cost[NMAX + 1][NMAX + 1];
int prefix_hash[NMAX + 1][SMAX + 1];
int pbase[SMAX + 1];
int dp[1 << NMAX][NMAX + 1], parent[1 << NMAX][NMAX + 1];

int get_hash(int h[], int left, int right) {
    return (h[right] - (ll) h[left - 1] * pbase[right - left + 1] % MOD + MOD) % MOD;
}

int get_cost(string& s1, string& s2) {
    int r = s1.size() - 1, l = 0;
    int hs1 = 0, hs2 = 0, pbase = 1, maxi = 0;
    while(l < (int) s2.size() && r >= 0) {
        hs1 = (hs1 + (ll) pbase * (s1[r] - 'A' + 1)) % MOD;
        hs2 = ((ll) hs2 * BASE + (s2[l] - 'A' + 1)) % MOD;
        l++;
        r--;
        pbase = (ll) pbase * BASE % MOD;
        if(hs1 == hs2) {
            maxi = l;
        }
    }
    return s2.size() - maxi;
}

bool is_substring(int i, int j) {
    int sz = a[i].size();
    for(int pos = sz; pos <= a[j].size(); pos++) {
        if(get_hash(prefix_hash[j], pos - sz + 1, pos) == prefix_hash[i][sz]) {
            return true;
        }
    }
    return false;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    pbase[0] = 1;
    for(int i = 1; i <= SMAX; i++) {
        pbase[i] = (ll) pbase[i - 1] * BASE % MOD;
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= a[i].size(); j++) {
            prefix_hash[i][j] = ((ll) prefix_hash[i][j - 1] * BASE + (a[i][j - 1] - 'A' + 1)) % MOD;
        }
    }

    for(int i = 1; i <= n; i++) {
        bool ok = true;
        for(int j = 1; j <= n; j++) {
            if(i != j && is_substring(i, j)) {
                ok = false;
            }
        }
        if(ok) {
            b[++ind] = a[i];
        }
    }

    for(int i = 1; i <= ind; i++) {
        a[i] = b[i];
    }
    n = ind;

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            if(i != j) {
                cost[i][j] = get_cost(a[i], a[j]);
            }
        }
    }

    for(int mask = 1; mask < (1 << n); mask++) {
        for(int i = 1; i <= n; i++) {
            dp[mask][i] = INF;
        }
    }
    for(int i = 1; i <= n; i++) {
        dp[1 << (i - 1)][i] = a[i].size();
    }
    for(int mask = 1; mask < (1 << n); mask++) {
        for(int i = 1; i <= n; i++) {
            if(mask >> (i - 1) & 1) {
                for(int j = 1; j <= n; j++) {
                    if(i != j && (mask >> (j - 1) & 1)) {
                        int cost_here = dp[mask ^ (1 << (i - 1))][j] + cost[j][i];
                        if(cost_here < dp[mask][i]) {
                            dp[mask][i] = cost_here;
                            parent[mask][i] = j;
                        }
                    }
                }
            }
        }
    }

    answer = INF;
    for(int i = 1; i <= n; i++) {
        if(dp[(1 << n) - 1][i] < answer) {
            answer = dp[(1 << n) - 1][i];
            start = i;
        }
    }

    int mask = (1 << n) - 1;
    while(start > 0) {
        int j = parent[mask][start];
        int stop = (j == 0 ? 0 : a[start].size() - cost[j][start]);
        for(int i = a[start].size() - 1; i >= stop; i--) {
            answer_s += a[start][i];
        }
        mask ^= (1 << (start - 1));
        start = j;
    }
    reverse(answer_s.begin(), answer_s.end());
    assert(answer_s.size() == answer);
    cout << answer_s << '\n';
    return 0;
}