Cod sursa(job #315299)

Utilizator FlorianFlorian Marcu Florian Data 14 mai 2009 21:48:23
Problema ADN Scor 100
Compilator cpp Status done
Runda Arhiva de probleme Marime 3.74 kb
using namespace std;
#include<cstdio>
#include<vector>
#include<algorithm>
#include<string.h>
#define MAX_N 18
#define PN (1<<N)
#define MAX_S 30002
#define bit(x) (1<<(x-1))
#define pb push_back
#define mk make_pair
#define f first
#define s second
#define Inf 0x3f3f3f3f
#define Mod1 666013
#define Mod2 100007
#define B1 26
#define B2 71
char M[MAX_N+2][MAX_S];
char X[MAX_N+2][MAX_S];
int N, P;
int dp[(1<<18)+4][MAX_N];
short int pre[(1<<18)+4][MAX_N];
vector<pair<int,int> >G[MAX_N+2];
int pref[MAX_S];
int C[MAX_N+2][MAX_N+2];
inline void prefix(char s[], int N)
{
	int i,k=0;
	pref[1] = 0;
	for(i=2;i<=N;++i)
	{
		while(k && s[i] != s[k+1]) k = pref[k];
		if(s[i] == s[k+1]) ++k;
		pref[i] = k;
	}
}
inline int KMP(char a[], int N, char b[], int M) // returneaza 1, daca b[] e inclus in a[]
{
	memset(pref,0,sizeof(pref));
	// b[] e modelul
	prefix(b,M);
	int i,k=0;
	for(i=1;i<=N;++i)
	{
		while(k && a[i] != b[k+1]) k = pref[k];
		if(a[i] == b[k+1]) ++k;
		if(k == M) return 1; // B[] este inclus in A[]
	}
	return 0; // B[] nu este inclus in A[]
}
inline int cost(char a[], int N, char b[], int M)
{
	//returnez lungimea celui mai lung prefix a lui B care este sufix a lui A
	int P1=1, P2=1, sol = 0;
	int hash1a, hash2a, hash1b, hash2b;
	hash1a = hash2a = hash1b = hash2b = 0;
	int i,j;
	for(i = N, j = 1; j<=M  && i>0; --i,++j)
	{
		hash1a += (a[i] * P1) % Mod1; hash1a %= Mod1;
		hash2a += (a[i] * P2) % Mod2; hash2a %= Mod2;
		hash1b = (hash1b * B1) % Mod1;
		hash1b = (hash1b + b[j]) % Mod1;
		hash2b = (hash2b * B2) % Mod2;
		hash2b = (hash2b + b[j]) % Mod2;
		if(hash1a == hash1b && hash2a == hash2b) sol = j;
		P1 = (P1 * B1) % Mod1; P2 = (P2*B2)%Mod2;
	}
	return sol;
}
void read()
{
	freopen("adn.in","r",stdin);
	freopen("adn.out","w",stdout);
	scanf("%d\n",&P);
	int i;
	for(i=1;i<=P;++i)
	{
		scanf("%s\n",M[i]+1);
		M[i][0] = '!';
	}
}
void eliminare() // elimin cuvintele incluse unul in altul
{
	int i,j,n,m;
	for(i=1;i<=P;++i)
		for(j=1, n=strlen(M[i])-1;j<=P && M[i][0]=='!';++j)
			if(i!=j && M[j][0] == '!') //daca i e inclus in j
			{
				m = strlen(M[j])-1;
				if(n > m) continue;
				if(KMP(M[j],m,M[i],n)) M[i][0] = '*';
			}
	N = 0;
	for(i=1;i<=P;++i)
		if(M[i][0] != '*')
		{
			n = strlen(M[i])-1;
			++N;
			for(j=0;j<=n;++j) X[N][j] = M[i][j];
		}
}
void construire()
{
	// construiesc graful . 
	// i -> j, de cost c <=> sufixul lui i este prefix a lui j, de lg c
	int i,j, cst,n,m;
	for(i=1;i<=N;++i)
		for(n = strlen(X[i])-1,j=1;j<=N;++j)
		if(i!=j)
		{
			m = strlen(X[j])-1;
			cst = cost(X[i],n,X[j],m);
			G[i].pb(mk(j,cst));
			C[i][j] = cst;
		}
}
int sol[20], nr;
void recon(int i, int j)
{
	if(pre[i][j] != j) recon(i - bit(j), pre[i][j]);
	sol[++nr] = j;
}
void dinamica()
{
	int i,j,k,y, p;
	for(i=1;i<=(1<<N);++i) for(j=1;j<=N;++j) dp[i][j] = -Inf;
	for(i=1;i<=N;++i) { dp[bit(i)][i] = 0; pre[bit(i)][i] = i; }
	for(i=1;i<=PN;++i)
		for(j=1;j<=N;++j)
		{
			if(dp[i][j] == -Inf) continue;
			for(k=0;k<G[j].size();++k)
			{
				y = G[j][k].f;
				if(bit(y) & i) continue;
				if(bit(y) + i > PN) continue;
				if(dp[i][j] + G[j][k].s <= dp[i+bit(y)][y]) continue;
				dp[i+bit(y)][y] = dp[i][j] + G[j][k].s;
				pre[i+bit(y)][y] = j;
			}
		}
	p = -1;
	for(i=1;i<=N;++i) if(p == -1 || dp[PN-1][i] > dp[PN-1][p]) p = i;
	recon((1<<N)-1,p);	
}
/*void debugsol()
{
	int i;
	for(i=1;i<=nr;++i) printf("%d ",sol[i]);
}
void deb()
{
	int i;
	for(i=1;i<=N;++i)
		printf("%s\n",X[i]+1);
}*/
void det_sol()
{
	int i;
	printf("%s",X[sol[1]]+1);
	for(i=2;i<=nr;++i)
		printf("%s",X[sol[i]] + 1+C[sol[i-1]][sol[i]]);
	
}
int main()
{
	read();
	eliminare();
	construire();
	dinamica();
	det_sol();
	return 0;
}