Cod sursa(job #2961409)

Utilizator ViAlexVisan Alexandru ViAlex Data 6 ianuarie 2023 14:30:33
Problema ADN Scor 100
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 3.04 kb
#include<iostream>
#include<deque>
#include<algorithm>
#include<vector>
#include<fstream>
using namespace std;

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

const int mx = 18;
int n;
vector<string> v;

// dist[i][j] - the distance between the nodes i and j (the number of characters saved by merging string i and j)
int dist[mx][mx];

// dp[node][mask] - the maximum cost of a path ending in node and passing through all nodes in mask.
int dp[mx][1<<mx];

// prv[node][mask] - the next to last node of the path that ends in node and connects all nodes in mask.
int prv[mx][1<<mx];

bool contained[mx];

void read(){
	in>>n;	
	v.resize(n);
	for(int i=0;i<n;i++){
		in>>v[i];
	}
}

// The number of characters that can be saved if the two strings are merged together. 
// example : abcde merged by cdefg is abcdefg
// the cost is "fg" - 2
// The problem now becomes finding a path that maximizes the number of characters saved.
int cost(const string&a, const string&b){
	// First compute the lsp of the second string.
	// lsp(i) - the longest preffix that is also a suffix 
	// of the string formed by the first i+1 characters.
	vector<int> lsp(b.length());
	lsp[0] = 0;

	int i = 0;
	int j = 1;

	while(j<b.length()){
		if(b[i] == b[j]){
			i++;
			lsp[j] = i;
			j++;
		}
		else{
			if(i==0){
				lsp[j] = 0;	
				j++;
			}	
			else{
				i = lsp[i-1];
			}
		}
	}


	int result = 0;
	// Now compute kmp.
	i = 0 ,j = 0;
	while(i<a.length()){
		if(a[i] == b[j]){
			i++;
			j++;

			if(j==b.length())
				return j;
		}
		else{
			if(j==0){
				i++;	
			}	
			else{
				j = lsp[j-1];
			}
		}
	}
	return j;
}

void compute_distances(){
	for(int i=0;i<n;i++){
		for(int k=0;k<n;k++){
			if(i==k)
				continue;
			dist[i][k] = cost(v[i],v[k]);
			if(dist[i][k] == v[k].length()){
				contained[k] = true;
			}
		}
	}
}

void solve(){
	compute_distances();
	vector<string> correct;
	for(int i=0;i<n;i++){
		if(!contained[i])
			correct.push_back(v[i]);
	}
	
	v = correct;
	n = correct.size();
	compute_distances();
	int all = (1<<n) -1;

	for(int mask = 0 ; mask<=all; mask++){
		for(int ending=0;ending<n;ending++){

			if((mask & (1<<ending)) == 0)
				continue;

			for(int previous=0;previous<n;previous++){
				if(previous == ending)
					continue;

				if((mask & (1<<previous)) == 0)
					continue;
				int c = dp[previous][mask ^ (1<<ending)] + dist[previous][ending];
				if(dp[ending][mask] <= c){
					dp[ending][mask] = c;
					prv[ending][mask] = previous; 	
				}
			}
		}
	}

	// Find the best node to end at.
	int current = 0; 
	for(int i=0;i<n;i++){
		if(dp[current][all]<dp[i][all]){
			current = i;
		}
	}

	// Rebuild path.
	vector<int> path;
	int mask = all;

	while(mask!=0){
		path.push_back(current);
		int prev_mask = mask ^ (1<<current);
		int prev_node = prv[current][mask];

		current = prev_node;
		mask = prev_mask;
	}

	reverse(path.begin(),path.end());
	int skip_next = 0; 

	for(int i=0;i<n;i++){
		int node = path[i];
		string s = v[node].substr(skip_next);
		out<<s;
		if(i!=n-1){
			skip_next = dist[node][path[i+1]];
		}
	}
}


int main(){
	read();
	solve();


	return 0;
}