题意:给三个长度相等的串A,B,C,已知他们与原串的海明距离不大于d,要求还原出字典序最小的原串。
贪心。
对于原串S的每一位来说,有四种可能性:
S[i]=A
S[i]=A[i]
S[i]=B[i]
S[i]=C[i]
除此之外,三个串在第i位上有五种可能的相同:
A[i],B[i],C[i]互不相等
A[i]=B[i]
A[i]=C[i]
B[i]=C[i]
A[i]=B[i]=C[i]
分别设这五种情况的个数为tp[0..4]。
针对五种情况我们开始从前往后贪心:
注意到每一位的取舍没有前效性,我们只需要记录后缀的tp即可,每次贪心的选取字典序最小的可行解。
初始状态设三个串和原串的最大距离为dp[0..2]。
从小到大枚举当前位每一个可行字符,判断方法如下:
设
r[0]=dp[0]-tp[3]
r[1]=dp[1]-tp[2]
r[2]=dp[2]-tp[1]
得到r[0..2]即为第i个串经过这次决策后的最小值
显然每个值需要>=0。
然而由于三者互相约束,我们至多可以出现一个负数,这样是可以这个负数的影响消除的
1. 如果出现两个以上的负数,判断不合法
2. 由于存在tp[0]的情况,每次修改对三个串对原串的海明距离至少+2
显然需要成立:r[0]+r[1]+r[2]>=2*tp[0]
3. 每个修改至少对原串的海明距离+1
需要成立:r[0]+r[1]+r[2]-max(r) >= tp[0]
正确性显然。
综上,算法复杂度O(4*N)
#include <bits/stdc++.h>
using namespace std;
const int MAXN=100005;
char A[3][MAXN], ans[MAXN];
char v[MAXN][4];
int tp[5], dp[4], num[MAXN];
int n, d;
bool check(){
for(int i=0; i<3; i++){
if(dp[i]<0) return false;
}
int r[3]={dp[0]-tp[3],dp[1]-tp[2],dp[2]-tp[1]};
int cnt=0;
for(int i=0; i<3; i++) if(r[i]<0) cnt++;
if(cnt>=2) return false;
for(int i=0; i<3; i++) if(r[i]<0){
for(int j=0; j<3; j++) if(i!=j){
r[j]+=r[i];
}
r[i]=0;
}
cnt=0;
for(int i=0; i<3; i++) if(r[i]<0) cnt++;
if(cnt) return false;
sort(r,r+3);
return min((r[0]+r[1]+r[2])/2,r[0]+r[1])>=tp[0];
}
int main(){
while(cin>>n>>d&&n){
for(int i=0; i<3; i++){
scanf("%s",A[i]);
dp[i]=d;
}
memset(tp,0,sizeof tp);
for(int i=0; i<n; i++){
int s=0;
for(int j=0; j<3; j++){
for(int k=j+1; k<3; k++){
if(A[j][i]==A[k][i]){
s+=j+k;
}
}
}
s=min(s,4);
num[i]=s; tp[s]++;
v[i][0]='A';
for(int j=0; j<3; j++){
v[i][j+1]=A[j][i];
}
sort(v[i],v[i]+4);
}
for(int i=0; i<n; i++){
tp[num[i]]--;
for(int j=0; j<4; j++){
for(int k=0; k<3; k++){
if(A[k][i]!=v[i][j]){
dp[k]--;
}
}
if(check()){
ans[i]=v[i][j];
goto ok;
}
for(int k=0; k<3; k++){
if(A[k][i]!=v[i][j]){
dp[k]++;
}
}
}
puts("-1");
goto failed;
ok:;
}
ans[n]=0;
puts(ans);
failed:;
}
return 0;
}