題面
Time limit per test: 2 seconds
Memory limit per test: 256 megabytes
Description
You are given n strings a1,a2,…,an: all of them have the same length m. The strings consist of lowercase English letters.
Find any string s of length m such that each of the given n strings differs from s in at most one position. Formally, for each given string ai, there is no more than one position j such that ai[j]≠s[j].
Note that the desired string s may be equal to one of the given strings ai, or it may differ from all the given strings.
For example, if you have the strings abac and zbab, then the answer to the problem might be the string abab, which differs from the first only by the last character, and from the second only by the first.
Input
The first line contains an integer t (1≤t≤100) — the number of test cases. Then t test cases follow.
Each test case starts with a line containing two positive integers n (1≤n≤10) and m (1≤m≤10) — the number of strings and their length.
Then follow n strings ai, one per line. Each of them has length m and consists of lowercase English letters.
Output
Print t answers to the test cases. Each answer (if it exists) is a string of length m consisting of lowercase English letters. If there are several answers, print any of them. If the answer does not exist, print "-1" ("minus one", without quotes).
Example
input
5
2 4
abac
zbab
2 4
aaaa
bbbb
3 3
baa
aaa
aab
2 2
ab
bb
3 1
a
b
c
output
abab
-1
aaa
ab
z
Note
The first test case was explained in the statement.
In the second test case, the answer does not exist.
題意
找出一個長度同樣為 m 的字符串 t
使得字符串 t 與給定的 n 個字符串最多只相差一個字符
解題思路 1 (思維)
如果存在答案,則可以分下面三種情況
1、所有字符串相同,則答案就是任意一個字符串。
2、字符串兩兩之間只差 1 位,說明所有字符串只在那一位上存在差別,其余部分保持不變,那一位輸出任意字符均可(也可以任意輸出其中一個字符串)。
3、字符串兩兩之間存在差 2 位的情況,可以想到,答案只可能有兩種情況,即除了這兩位外其余保持不變,這兩位交叉互取一位即可,兩種答案都判斷一遍。
除以上情況外,其余均為答案不存在
對於第 3 種情況的例子:
aaba
abaa
這兩個串在第 2、3 兩個位置不同
保持第 1、4 兩個位置不變
則答案只可能是交叉互取一位的結果,即 aaaa (第 2 位取自第一個串,第 3 位取自第二個串) 或者 abba (第 2 位取自第二個串,第 3 位取自第一個串)
如果存在其余字符串,拿這兩個結果都判斷一遍即可,只要有一個滿足題意就可以輸出
子函數寫的有點多別介意
#include<bits/stdc++.h>
using namespace std;
int n,m;
char str[15][15],ans[15];
bool check() //檢查ans是否合法
{
for(int i=0;i<n;i++)
{
int d=0;
for(int j=0;j<m;j++)
{
if(str[i][j]!=ans[j])
d++;
}
if(d>1) //如果與某個字符串差值大於1位
return false;
}
return true;
}
void deal(int p1,int p2) //對於差值等於2的兩個串處理
{
char tmp[2][2];
int flag=0,pos[2];
for(int i=0;i<m;i++)
if(str[p1][i]!=str[p2][i])
{
pos[flag]=i; //記錄位置
tmp[flag][0]=str[p1][i]; //記錄有差別的兩個字符
tmp[flag][1]=str[p2][i];
flag++;
}
else
ans[i]=str[p1][i];
ans[m]='\0';
ans[pos[0]]=tmp[0][0];
ans[pos[1]]=tmp[1][1]; //使用第一種交叉方案
if(check())
{
cout<<ans<<'\n';
return;
}
ans[pos[0]]=tmp[0][1];
ans[pos[1]]=tmp[1][0]; //使用第二種交叉方案
if(check())
{
cout<<ans<<'\n';
return;
}
cout<<-1<<'\n'; //兩種都不可行,說明不存在答案
}
int cal(int p1,int p2) //計算兩個串的差值
{
int res=0;
for(int i=0;i<m;i++)
if(str[p1][i]!=str[p2][i])
res++;
return res;
}
void solve()
{
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>str[i];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++) //雙重循環查找兩兩之間的差值
{
if(i==j)
continue;
int d=cal(i,j);
if(d>2) //如果存在差兩個以上,則一定不存在答案
{
cout<<-1<<'\n';
return;
}
else if(d==2) //存在差值等於2的
{
deal(i,j);
return;
}
}
cout<<str[0]<<'\n'; //任意輸出一個
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}
解題思路 2 (暴力)
既然答案與每個字符串只差至多一位
那就拿第一個串作為基准,要么不改,要么只改一位,a到z每種字符都試一遍,暴力查找是否存在答案即可
#include<bits/stdc++.h>
using namespace std;
int n,m;
char str[15][15],ans[15];
bool check() //檢查ans是否合法
{
for(int i=1;i<n;i++) //取第一個串作為基准,則從第二個串開始匹配即可
{
int d=0;
for(int j=0;j<m;j++)
{
if(str[i][j]!=ans[j])
d++;
}
if(d>1) //如果與某個字符串差值大於1位
return false;
}
return true;
}
void solve()
{
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>str[i];
ans[m]='\0';
for(int i=0;i<m;i++)
ans[i]=str[0][i]; //取第一個串作為基准
for(int i=0;i<m;i++)
{
for(char c='a';c<='z';c++)
{
ans[i]=c; //第i位改成c時
if(check())
{
cout<<ans<<'\n';
return;
}
}
ans[i]=str[0][i]; //改回
}
cout<<-1<<'\n'; //不存在答案
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}
解題思路 3 (dfs)
可以想到,如果存在答案,那么最優解的每一位字符都在給定的 n 個串中對應的那一位上出現過
因為數據范圍小,可以使用 set 記錄每一位出現過的字符,並以此進行深搜
深搜從第 1 位開始往后搜索答案,vis 數組以二進制記錄當前狀態下第 i 個串與答案在哪一位存在差別
注:因為返回時沒有清空,所以每次調用 dfs 搜索第 p 位時需要把第 p 位及以后的搜索狀態全部清除,也就是對 (1<<p) 進行取模
#include<bits/stdc++.h>
using namespace std;
int n,m,vis[15];
char str[15][15],ans[15];
set<char> st[15];
bool dfs(int p,char c) //如果第p位是字符c時是否合法
{
for(int i=1;i<=n;i++)
vis[i]=vis[i]%(1<<p); //僅保留位置p之前的搜索記錄
for(int i=1;i<=n;i++)
{
if(str[i][p]!=c) //如果第p位不是字符c,說明第i個字符串與目前的答案有差別,需要標記
{
if(vis[i]!=0) //如果第i個字符串前面已經標記過
return false; //就說明與答案存在兩個及以上的差別,與題意不符,返回false
vis[i]=(1<<p); //否則,標記在第p位與答案存在差別
}
}
if(p==m)
return true; //如果p是最后一位,且前面條件都滿足,說明找到了答案
for(char c:st[p+1])
{
if(dfs(p+1,c)) //檢查第p+1位為字符c是是否合法
{
ans[p+1]=c; //合法時說明已經找到答案,賦值后返回true
return true;
}
}
return false; //都不合法,返回false
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>(str[i]+1);
for(int i=1;i<=m;i++)
st[i].clear();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
st[j].insert(str[i][j]); //每一位出現過的字符都存在set中
memset(vis,0,sizeof vis);
ans[m+1]='\0';
for(char c:st[1])
if(dfs(1,c)) //第i位為c時是否合法
{
ans[1]=c;
cout<<(ans+1)<<'\n'; //如果合法則直接輸出答案即可
return;
}
cout<<-1<<'\n'; //沒有找到,輸出-1
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}