一道字符串贪心题

12 篇文章 0 订阅
4 篇文章 0 订阅

题意

DNA序列
dna.c/cpp/in/out
时间限制:2s
空间限制:2G
题目描述
2018年10月,MIT建立了最新的纳米科技研究中心MIT.nano。此后,不断有新的研究成果在此产生。
有一天,研究者发现了一种新的生物,这种生物的基因中含有 条DNA序列,每一条都有一定的长度,
科学家们可以将每条DNA序列切断,从而取出它的一个非空前缀。此后,他们可以将这些前缀按任意
顺序连结起来形成一条完整的DNA序列,这样的DNA序列对治疗癌症有很大的作用。
每条DNA序列都仅包含大写字母“A”,“C”,“G”,“T”。
科学家们很快发现,DNA序列的字典序越小,则治疗癌症的效果越好,他们想知道给定 条DNA序列,
他们能获得的字典序最小的DNA序列是什么。
输入格式
第一行一个正整数 n n n ,表示DNA序列的个数。
接下来 n n n行,每行一个非空字符串表示一条DNA,保证每个字符均为“A”,“C”, “G”,“T”中的一个。
输出格式
输出一行一个字符串,表示能获得的字典序最小的DNA序列

数据范围

1 < = n < = 50 , 1 < = 字 符 串 长 度 < = 50 1<=n<=50,1<=字符串长度<=50 1<=n<=50,1<=<=50

部分分:存在一种最优解按照 1 到 n 1到n 1n的顺序连接.

解法

首先考虑部分分,可以发现如果顺序确定,那么可以从 n 到 1 n到1 n1贪心枚举每个字符串选择的前缀是多少,时间复杂度 O ( n 4 ) O(n^4) O(n4),具体就是暴力枚举每个前缀,看放进现有答案是否最优.实现细节:注意每个字符串都要选.
然后问题就在于确定顺序.
对于每个字符串s,找到最短的前缀x,满足 x i n f < = s x^{inf}<=s xinf<=s,然后将字符串分成 x y + z x^y+z xy+z使得x不是z的前缀.
那么合并的顺序一定是按照 x i n f x^{inf} xinf字典序升序然后相同按照 z + U z+U z+U字典序降序,注意使用字母 U U U的原因是 U U U比四个字母都要大。
感性的理解就是这样,每次我们希望找个最小的 x x x,然后加入尽量多的 x x x,但是,加完之后我们希望能加的字符尽量小,所以我们把尾巴字典序最小的放后面。
注意实现细节:计算 x i n f x^{inf} xinf时,如果当前的 x x x和剩下的长度为 l e n len len的字符串在len长度内仍然没有比较出大小,那么这个 x x x也是合法的.然后就是 z z z, z z z的开头会固定在 ∣ x ∣ ∗ t + 1 |x|*t+1 xt+1的位置,这个是根据定义来的(这样讲虽然很奇怪,但是实际就是需要满足 x y + z = = s x^y+z==s xy+z==s)

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	char c=getchar();int t=0,f=1;
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m,pre[55],ord[55],pos[55],len[55];
string s[55],ans,tmp,bt,now,q[55],alfa,st,cpa,cpb,ans2;
int fc(int a,int b){
	cpa=q[a],cpb=q[b];
	int l1=cpa.size(),l2=cpb.size();
	while(cpa.size()!=cpb.size()){
		if(cpa.size()<cpb.size())cpa=cpa+q[a];
		else cpb=cpb+q[b];
	}
	int tmp=0;
	if(cpa>cpb)tmp=1;
	if(cpa<cpb)tmp=-1;
	return tmp;
}
bool cmp(int a,int b){
	if(fc(a,b)==0)return s[a].substr(pos[a])+'U'>s[b].substr(pos[b])+'U';
	else return fc(a,b)==-1;
}
int main(){
	//freopen("b.in","r",stdin);
	//freopen("b.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++){
		cin>>s[i];
		s[i]=s[i];
		m=max(m,(int)s[i].size());
	}
	if(m==1){
		sort(s+1,s+1+n);
		for(int i=1;i<=n;i++)putchar(s[i][0]);
		return 0;
	}
	for(int i=1;i<=n;i++){
		int l=s[i].size();
		for(int j=1;j<=l;j++){
			int flag=1;
			for(int k=j;k<l;k++){
				if(s[i][k]<s[i][k%j]){flag=0;break;}
				if(s[i][k]>s[i][k%j]){flag=1;break;}
			}
			if(flag){q[i]=s[i].substr(0,j);break;}
		}
		if(!q[i].size()){
			q[i]=s[i];
		}
		int l2=q[i].size(),j;
		for(j=l2;j<l;j+=l2){
			if(q[i]!=s[i].substr(j,l2)){
				pos[i]=j;break;
			}
		}
		if(!pos[i])pos[i]=j;
	}
	for(int i=1;i<=n;i++)ord[i]=i;
	sort(ord+1,ord+1+n,cmp);
	/*for(int i=1;i<=n;i++){
		cout<<s[ord[i]].substr(pos[ord[i]])<<' '<<s[ord[i]]<<endl;
	}*/
	ans=s[ord[n]][0];
	memset(pre,0,sizeof(pre));
	for(int i=n-1;i>0;i--){
		int j=1;tmp=s[ord[i]][0]+ans;
		bt=tmp;
		for(;j<s[ord[i]].size();j++){
			tmp=ans;
			for(int k=j;k>=0;k--)tmp=s[ord[i]][k]+tmp;
			if(bt>tmp){
				pre[ord[i]]=j;bt=tmp;
			}
		}
		for(int j=pre[ord[i]];j>=0;j--){
			ans=s[ord[i]][j]+ans;
		}
	}
	//if(ans2<ans)ans=ans2;
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值