度度熊與排列
度熊有一個機器,這個機器有一個 1∼M 的排列 p[1..M] 當作參數,若丟進一個長度為 M 的字符串,此機器會將此字符串重新排列后再輸出,重新排列的方式為:原本第 i 個位置的字符會變到第 p[i] 個位置。
舉例來說,當 M=3,p[1]=3,p[2]=1,p[3]=2,那么丟 "abc" 進入這個機器后,機器會輸出"bca";若丟進的是 "ded",那么機器會輸出 "edd"。
某天,度熊不小心忘記這個機器的參數了,只記得參數的長度是 M,於是他丟了 N 長度為 M 的字符串進去,並記錄下對於每個字符串機器的輸出結果,請你根據這些結果,幫度熊找回這個機器的參數。若有多組參數都滿足度熊的記錄,請輸出字典序最小的排列作為參數。若並不存在任何參數滿足度熊的記錄,請輸出 −1。
注:對於兩個相異的排列a: a[1..M] 和 b[1..M],我們稱 a 比 b 小當且僅當 存在一個 i,滿足對於所有小於 i 的 j 都有 aj=bj 且 ai<bi。
有多組詢問,第一行包含一個正整數 T 代表有幾組詢問。
每組詢問的第一行包含兩個正整數 N,M,分別代表度熊丟進機器的字符串數目以及參數的長度。接下來還有 2×N 行,每行有一個長度為 M 的字符串,當中的第 2×i−1 行的字符串代表度熊丟進去機器的第 i 個字符串,而第 2×i 行的字符串代表機器對於第 i 個字符串的輸出結果。
-
1≤T≤100
-
1≤N≤20
-
1≤M≤50
-
字符串由英文小寫字母('a' 至 'z') 組成
對於每一個詢問,輸出一行,若不存在任何參數滿足度熊的記錄,這行只包含一個整數 −1。否則這行包含一個排列,代表此機器所有可能的參數中字典序最小的那個。
4 1 3 abc bca 2 4 aaab baaa cdcc cccd 3 3 aaa aaa bbb bbb ccc ccc 1 1 a z
3 1 2 2 4 3 1 1 2 3 -1
3 1 2
2 4 3 1
1 2 3
-1
Note
第一組詢問中, p[1]=3,p[2]=1,p[3]=2p[1]=3,p[2]=1,p[3]=2 是唯一的機器可能的參數。 第二組詢問中, p=[2,4,3,1]p=[2,4,3,1] 和 p=[3,4,2,1]p=[3,4,2,1] 都是機器可能的參數,不過 [2,4,3,1][2,4,3,1] 的字典序比 [3,4,2,1][3,4,2,1] 還小,故必須輸出 2,4,3,1。
思路:
- 觀察發現,在序列sv中的第i項數字確定后,原字符串的第i項必然是新字符串的sv[i]項
- 從1到M逐步構造序列的每一項,如果有字符串不匹配,則從小到大更新的構造sv[i]
- 當sv[i]無法更新時說明沒有一個可以匹配到所有字符串的序列,此時返回-1
- 當sv[1~M]都更新完成,打印sv數組即為答案
代碼如下:
//1002 #include<iostream> #include<cstring> #include<cstdio> using namespace std; char pre[25][55]; char nxt[25][55]; int v[55],sv[55]; int N,M; int j,k ; char c; bool bfs(int i) { if(i == M)return true; c = pre[0][i]; for(j = 0 ; j < M; j++){ if(c == nxt[0][j] && v[j] == -1){ v[j] = i; sv[i] = j; for(k = 1; k < N;k++){ if(pre[k][i] != nxt[k][sv[i]]){ v[j] = -1;sv[i] = -1; break; }; } if(k != N)continue; if(bfs(i+1))return true; } } return false; } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); for(int i = 0 ; i < N;i++){ scanf("%s",pre[i]) ; scanf("%s",nxt[i]) ; } memset(v,-1,sizeof(v)); if(bfs(0)){ for(int i = 0 ; i < M;i++){ printf("%d",sv[i]+1); if(i == M-1) printf("\n"); else printf(" "); } }else{ printf("-1\n"); } } return 0; }