# Greedy# String# Counting

f437 - 1368 - DNA Consensus String

🔗 前往 ZeroJudge 原題

題目描述

題目給定 M 個長度為 N 的 DNA 序列 (由 A, T, G, C 組成),要求找出與這些序列漢明距離總和最小的字串 (共識字串),並輸出該字串以及最小的漢明距離總和 (共識錯誤)。如果有多個共識字串,則輸出字典序最小的那個。

解題思路

本題的核心思想是貪心策略。對於 DNA 序列的每一位,統計 A, T, G, C 四個鹼基出現的次數,選擇出現次數最多的鹼基作為共識字串對應位上的鹼基。然後計算共識字串與所有輸入序列的漢明距離總和。

具體步驟如下:

  1. 讀取輸入,包括 DNA 序列的數量 M 和長度 N,以及 M 個 DNA 序列。
  2. 建立一個二維陣列 dp[N][4],用於統計每一位上 A, T, G, C 四個鹼基的出現次數。
  3. 遍歷所有 DNA 序列,對於每個序列的每一位,將對應鹼基的計數加 1。
  4. 建立一個字串 aa,用於儲存共識字串。
  5. 遍歷每一位,找到出現次數最多的鹼基,將其添加到 aa 中。
  6. 計算共識字串 aa 與所有輸入序列的漢明距離總和 anss
  7. 輸出共識字串 aa 和共識錯誤 anss

複雜度分析

  • 時間複雜度: O(M * N),其中 M 是 DNA 序列的數量,N 是 DNA 序列的長度。需要遍歷所有 DNA 序列的每一位來統計鹼基出現次數,以及遍歷每一位來構建共識字串和計算漢明距離。
  • 空間複雜度: O(N),主要用於儲存 dp[N][4] 陣列和共識字串 aa

程式碼

#include <iostream>
#include <algorithm>
using namespace std;
string a[1001];
int dp[1001][4],t,x,y;
int main(){
	cin.tie(0); ios::sync_with_stdio(false);
	cin >> t;
	while(t--){
		cin >> x >> y;
		for(int i=0;i<1001;++i)
			for(int j=0;j<4;++j)
				dp[i][j]=0;
		int ans[y]={0},anss=0;
		string aa;
		for(int i=0;i<x;++i){
			cin >> a[i];
			for(int j=0;j<y;++j){
				if(a[i][j]=='A')
					++dp[j][0];
				else if(a[i][j]=='C')
					++dp[j][1];
				else if(a[i][j]=='G')
					++dp[j][2];
				else
					++dp[j][3];
			}
		}
		for(int i=0;i<y;++i){
			ans[i]=max(max(dp[i][0],dp[i][1]),max(dp[i][2],dp[i][3]));
			for(int j=0;j<4;++j){
				if(ans[i]==dp[i][j]){
					if(j==0){
						aa+='A';
						anss+=x-dp[i][j];
						break;
					}
					else if(j==1){
						aa+='C';
						anss+=x-dp[i][j];
						break;
					}
					else if(j==2){
						aa+='G';
						anss+=x-dp[i][j];
						break;
					}
					else{
						aa+='T';
						anss+=x-dp[i][j];
						break;
					}
				}
			}
		}
		cout << aa << "\n" << anss << "\n";
	}
}

Discussion