Cod sursa(job #2962755)

Utilizator anne_marieMessner Anne anne_marie Data 9 ianuarie 2023 02:01:20
Problema ADN Scor 0
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 7.97 kb
// adn - https://infoarena.ro/job_detail/2962657?action=view-source
/*Nodurile:
X : C A A A A B C D E
Y : D E A C C C
Muchiile:
X -> Y : 2 (sf lui X cu în lui Y)
Y -> X : 1 (sf lui Y cu în lui X)
Caut o parcurgere a grafului astfel încât suma muchiilor sa fie maxima și fiecare nod parcurs o singura data

        dp[i][conf] = suma maxima a muchiilor pana în i având vizitate nodurile din conf
        dp[i][conf] = 0 (inițial)
Conf_nou += 1 << vec
        dp[vec][conf_nou] = max(dp[vec][conf_nou], dp[i][conf] + muchie(i, vec))

For pt conf (de la 0 la 2^n -1)
For pt I (de la 0, la n - 1)
verify dacă i în conf
for(auto vec: ad[I])
verify faca vec nu e in conf
        recurenta
*/
#include <iostream>
#include <fstream>
#include <climits>
#include <vector>
#include <algorithm>
using namespace std;
ifstream fin("adn.in");
ofstream fout("adn.out");


int n;
string adn[20];
int cost[20][20];
int dp[20][1 << 20];
vector<string> new_adn;
bool eliminated[20];
pair<int, int> father[20][1 << 20];
vector<int> path;

int max_length = 30000;
const int mod1 = 30103;
const int mod2 = 666013;
int suf_mod1[30005], suf_mod2[30005], pref_mod1[30005], pref_mod2[30005];
int pow26_mod1[30005], pow26_mod2[30005];
// ------------------------------ Word Distance ---------------------------------
//int wordDistance(int word1, int word2) {
//    // Calculez subsecventa comuna intre sfarsitul primului cuvant si inceputul celui de-al doilea cuavnt
//    string str1 = adn[word1];
//    string str2 = adn[word2];
//    int w_size1 = str1.size();
//    int w_size2 = str2.size();
//
//    for (int i = 0; i < w_size1; i++) {
//        // Verific suprapunerea de la pozitia i in dreapta
//        bool same = true;
//
//        for (int j = i; (j < w_size1) && (j - i < w_size2); j++) {
//            // Verific daca cele doua cuvinte sunt identice
//            if (str1[j] != str2[j - i]) {
//                same = false;
//                break;
//            }
//        }
//
//        if (same) {
//            // Verific daca al doilea cuvant se gaseste in interiorul primului
//            if (w_size1 - i >= w_size2) {
//                return -1;
//            }
//            // Lungimea comuna a celor doua cuvinte
//            return w_size1 - i;
//        }
//    }
//    // Cuvintele nu au litere in comun
//    return 0;
//}
// -------------------------------- Rabin Karp --------------------------------------

void pow() {
    // calculeaza puterile lui 26
    pow26_mod1[0] = 1;
    pow26_mod2[0] = 1;
    for(int i = 1; i < max_length; i++)
    {
        pow26_mod1[i] = (pow26_mod1[i - 1] * 26) % mod1;
        pow26_mod2[i] = (pow26_mod2[i - 1] * 26) % mod2;
}

}
int rabinKarp(int word1, int word2) {
    // Calculez subsecventa comuna intre sfarsitul primului cuvant si inceputul celui de-al doilea cuavnt
    string str1 = adn[word1];
    string str2 = adn[word2];
    int w_size1 = str1.size();
    int w_size2 = str2.size();

    // Initializez vectorii
    fill(suf_mod1, suf_mod1 + max_length + 1, 0);
    fill(suf_mod2, suf_mod2 + max_length + 1, 0);
    fill(pref_mod1, pref_mod1 + max_length + 1, 0);
    fill(pref_mod2, pref_mod2 + max_length + 1, 0);
    pow();

    // pref_mod[i] = prefixul lui str1 pana la poz i
    // suf_mod[i] = sufixul lui str2 de la poz i

    // Transform primul caracter in numar
    pref_mod1[0] = str2[0] - 'A';
    pref_mod2[0] = str2[0] - 'A';

    // Calculez restul prefixelor sirului str2
    for(int i = 1; i < w_size2; i++)
    {
        pref_mod1[i] = (pref_mod1[i - 1] * 26 + str2[i] - 'A') % mod1;
        pref_mod2[i] = (pref_mod2[i - 1] * 26 + str2[i] - 'A') % mod2;
    }

    // Calculez sufixele sirului str1
    for(int i = n - 1; i >= 0; i--)
    {
        suf_mod1[i] = ((str1[i] - 'A') * pow26_mod1[n - i - 1] + suf_mod1[i + 1]) % mod1;
        suf_mod2[i] = ((str1[i] - 'A') * pow26_mod2[n - i - 1] + suf_mod2[i + 1]) % mod2;
    }

    // Caut pozitia la care se suprapun
    for(int i = 0; i < n; i++)
    {
        if(w_size1 - i > w_size2)
            continue;
        if(suf_mod1[i] == pref_mod1[w_size1 - i - 1] && suf_mod2[i] == pref_mod2[w_size1 - i - 1])
        {
            if(w_size1 - i - 1 == w_size2 - 1) // str1 = str2
                return -1;
            return w_size1 - i; // str1 si str2 se suprapun
        }
    }
    return 0; // str1 si str2 nu se suprapun
}

// ------------------------------ Remake ADN ---------------------------------
void remakeADN() {
    new_adn.push_back("");
    // Daca sunt mai multe cuvinte identice, lasa doar prima aparitie
    for(int i = 1; i < n; i ++)
        for(int j = i + 1; j <= n; j ++) {
            if(adn[i] == adn[j])
                eliminated[j] = true;
        }
    for(int i = 1; i < n ; i ++){
        if(eliminated[i])
            continue;
        for(int j = 1; j <= n; j ++) {
            if(i == j || eliminated[j])
                continue;
            if(rabinKarp(i, j) == -1) {
                eliminated[j] = true;
                //cout << i << " " << j <<'\n';
            } else
            if(rabinKarp(j, i) == -1) {
                eliminated[i] = true;
                //cout << j << " " << i << '\n';
            }
        }
    }

    for(int i = 1; i <= n; i ++)
        if(!eliminated[i]) {
            new_adn.push_back(adn[i]);
        }
}


// ------------------------------ Main ---------------------------------
int main() {

    // Citesc string-urile de adn
    fin >> n;
    for(int i = 1; i <= n; i ++)
        fin >> adn[i];

    // Refac vectorul de string-uri astfel incat sa elimin string-urile ce se regasesc in interiorul altora
    remakeADN();
    n = new_adn.size() - 1;

    // Suprascriu adn cu new_adn
    for(int i = 1; i <= n ; i ++)
        adn[i] = new_adn[i];

    // Calculez costul distantelor dintre cuvinte
    for(int i = 0; i < n; i++)
        for(int conf = 0; conf < (1 << n); conf++)
            dp[i][conf] = -(1 << 30);
    for(int i = 0; i < n; i++)
        dp[i][(1 << i)] = 0;

    for(int i = 1; i < n; i ++)
        for(int j = i + 1; j <= n; j ++) {
            cost[i][j] = rabinKarp(i, j);
            cost[j][i] = rabinKarp(j, i);
        }



    // Caut o parcurgere a grafului astfel încât suma muchiilor sa fie maxima și fiecare nod parcurs o singura data
    // dp[i][conf] = suma maxima a muchiilor pana în i având vizitate nodurile din conf
    for(int conf = 0; conf <= (1 << n) - 1; conf ++)
        for(int i = 0; i < n; i ++)
            if(conf & (1 << i)) // Verific daca i este deja vizitat
                for(int j = 0; j < n; j ++)
                    if(cost[i + 1][j + 1] != -1 && !(conf & (1 << j))) { // Verific daca j este vecini si daca nu este vizitat
                        if(dp[j][conf + (1 << j)] < dp[i][conf] + cost[i + 1][j + 1])
                        {
                            father[j][conf + (1 << j)] = make_pair(i, conf);
                            dp[j][conf + (1 << j)] = dp[i][conf] + cost[i + 1][j + 1];
                        }
//                        int new_conf = conf + (1 << j);
//                        if(dp[j][new_conf] < dp[i][conf] + cost[i + 1][j + 1])
//                            father[j][new_conf] = make_pair(i, conf);
//                        dp[j][new_conf] = max(dp[j][new_conf], dp[i][conf] + cost[i + 1][j + 1]);

                    }

    int last_node = -1;
    int max_size = -1;
    // Caut maximul din dp[i][(1 << n> - 1]
    for(int i = 0; i < n; i ++)
        if(dp[i][(1 << n) - 1] > max_size) {
            max_size = dp[i][(1 << n) - 1];
            last_node = i;
        }

    int node = last_node;
    int conf = (1 << n) - 1;

    while(conf != 0) {
        path.push_back(node + 1);
        int new_conf = father[node][conf].second;
        node = father[node][conf].first;
        conf = new_conf;
    }

    reverse(path.begin(), path.end());

    // Reconstituire sir
    string result = "";
    result += adn[path[0]];
    for(int i = 1; i < path.size(); i ++) {
        for(int j = cost[path[i - 1]][path[i]]; j < adn[path[i]].size(); j ++) {
            result += adn[path[i]][j];
        }
    }
    fout << result;
    return 0;
}