# APCS實作題2022年6月第2題:字串解碼 > 日期:2023年9月23日 > 作者:王一哲 > 題目來源:111年6月實作題第2題 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=i400) <br /> ## 題目 ### 問題描述 有一個字串加密系統,對於一個長度為 $n$ 的字串 $S$ 和一個長度為 $n$ 的 01 字串 $e$,會產生一個加密過後的字串 $T$,其中加密流程為以下兩個步驟: 1. 如果字串 $e$ 中 $1$ 的出現次數是偶數,則直接進第二步驟,出現次數是奇數則將字串 $S$ 平分成兩等份,兩份順序交換後再接起來,如果字串長度是奇數,則最中間的字元不動位置。 2. 讓 $i$ 從 $1$ 到 $n$ 迭代,如果 $e[i] = 0$ 就將 $S$ 的第一個字元切掉並接到字串 $T$ 的最後一個字元,如果 $e[i] = 1$ 就將 $S$ 的最後一個字元切掉並接到字串 $T$ 的最後一個字元。 以範例一為例,陣列為 10110,其中 1 出現的次數為奇數,因此需要交換字串 $S$ 從 BCAAD 變為 ADABC。接下來執行第二階段,其中過程如下。 <center> | <center>i</center> | <center>1</center> | <center>2</center> | <center>3</center> | <center>4</center> | <center>5</center> | | - | - | - | - | - | - | | e | 1 | 0 | 1 | 1 | 0 | | S | ADAB | DAB | DA | D | 空字串 | | T | C | CA | CAB | CABA | CABAD | </center> 給由 $m$ 個 01 字串的加密表和按照順序加密原字串後得到加密過後的字串,請嘗試還原出原本的字串。 <br /> ### 輸入說明 第一行輸入兩個數字 $m, n ~(1 \leq m, n \leq 100)$,接下來有 $m$ 個長度為 $n$ 的 01 字串,最後輸入一個長度為 $n$ 的加密字串,字串均由大寫字母組成。 子題配分 - 60%:$m = 1$ - 40%:無額外限制 <br /> ### 輸出說明 輸出解碼後的字串。 <br /> ### 範例一:輸入 ``` 1 5 10110 CABAD ``` ### 範例一:正確輸出 ``` BCAAD ``` <br /> ### 範例二:輸入 ``` 3 6 111110 101101 000000 RETYWQ ``` ### 範例二:正確輸出 ``` QWERTY ``` <br /> ## 提示 範例測資一,如題目所述。 範例測資二過程如下: 1. e = 111110,出現的數量為奇數次,因此需要將字串 "QWERTY" 切半翻轉成 "RTYQWE",經過 01 字串的轉換後會得到 "EWQYTR"。 2. e = 101101, 出現的數量為偶數次,因此不用翻轉字串,經過 01 字串的轉換後會得到 "RETYWQ"。 3. e = 000000, 出現的數量為偶數自,因此不用翻轉字串,經過 01 字串的轉換後會得到 "RETYWQ"。 <br /><br /> ## Python 程式碼 ### 加密 依照題目的敘述,試著先寫出加密的程式碼,如果能夠從範例的輸出資料推出輸入資料,接下來再把程式碼運作的過程反過來,就能寫出解碼用的程式碼。 ```python= m, n = map(int, input().split()) # 讀取m, n ops = [] # 儲存操作過程的串列 for i in range(m): ops.append(input()) # 讀取操作過程 s = input() # 加密前的字串 t = "" # 加密後的字串 for i in range(m): # 依序讀取操作過程 num = 0 # 操作過程中 1 的數量 op = ops[i] # 第i個操作過程 for j in range(n): # 依序讀取 op 的內容,計算 1 的數量 if op[j] == '1': num += 1 if num%2 == 1: # 如果 1 的數量是奇數,將 s 分成兩半、前後交換 if n%2 == 0: s = s[n//2 :] + s[:n//2] else: s = s[n//2 + 1 :] + s[n//2] + s[:n//2] for j in range(n): # 依序讀取 op 的內容 if op[j] == '0': # 如果是 0,取下 s 最前面的字母加到 t 的最後面 t = t + s[0] s = s[1:] elif op[j] == '1': # 如果是 1,取下 s 最後面的字母加到 t 的最後面 t = t + s[-1] s = s[:-1] if i < m-1: # 如果還有未讀取的操作過程,重設 s 和 t 的內容 s = t t = "" print(t) # 印出加密後的字串 ``` <br /><br /> ### 解碼 於 ZeroJudge 測試結果,最長運算時間約為 30 ms,使用記憶體最多約為 3.4 MB,通過測試。 ```python= m, n = map(int, input().split()) # 讀取m, n ops = [] # 儲存操作過程的串列 for i in range(m): ops.append(input()) # 讀取操作過程 s = "" # 加密前的字串,最後要輸出的答案 t = input() # 加密後的字串 for i in range(m): # 倒序讀取操作過程 num = 0 # 操作過程中 1 的數量 op = ops[m-i-1] # 第 m-i-1 個操作過程 for j in range(n): # 倒序讀取 op 的內容,計算 op 中 1 的數量,同時將 t 還原為 s if op[n-j-1] == '0': # 如果是 0,取下 t 最後面的字母加到 s 的最前面 s = t[-1] + s t = t[:-1] elif op[n-j-1] == '1': # 如果是 1,取下 t 最後面的字母加到 s 的最後面 num += 1 s = s + t[-1] t = t[:-1] if num%2 == 1: # 如果 1 的數量是奇數,將 s 分成兩半、前後交換 if n%2 == 0: s = s[n//2 :] + s[:n//2] else: s = s[n//2 + 1 :] + s[n//2] + s[:n//2] if i < m-1: # 如果還有未讀取的操作過程,重設 s 和 t 的內容 t = s s = "" print(s) # 印出解碼後的字串 ``` <br /><br /> ## C++ 程式碼 ### 加密 ```cpp= #include <iostream> #include <string> using namespace std; int main() { int m, n; cin >> m >> n; // 讀取m, n string ops[m]; // 儲存操作過程的串列 for(int i=0; i<m; i++) cin >> ops[i]; // 讀取操作過程 string s; cin >> s; // 加密前的字串 string t = ""; // 加密後的字串 for(int i=0; i<m; i++) { // 依序讀取操作過程 int num = 0; // 操作過程中 1 的數量 string op = ops[i]; // 第i個操作過程 for(auto c : op) { // 依序讀取 op 的內容,計算 1 的數量 if (c == '1') num++; } if (num%2 == 1) { // 如果 1 的數量是奇數,將 s 分成兩半、前後交換 if (n%2 == 0) s = s.substr(n/2, n/2) + s.substr(0, n/2); else s = s.substr(n/2 + 1, n/2) + s[n/2] + s.substr(0, n/2); } for(auto c : op) { // 依序讀取 op 的內容 if (c == '0') { // 如果是 0,取下 s 最前面的字母加到 t 的最後面 t = t + s[0]; s = s.substr(1, s.size()-1); } else if (c == '1') { // 如果是 1,取下 s 最後面的字母加到 t 的最後面 t = t + s[s.size()-1]; s = s.substr(0, s.size()-1); } } if (i < m-1) { // 如果還有未讀取的操作過程,重設 s 和 t 的內容 s = t; t = ""; } } cout << t << endl; // 印出加密後的字串 return 0; } ``` <br /><br /> ### 解碼 於 ZeroJudge 測試結果,最長運算時間約為 5 ms,使用記憶體最多約為 368 kB,通過測試。 ```cpp= #include <iostream> #include <string> using namespace std; int main() { int m, n; cin >> m >> n; // 讀取m, n string ops[m]; // 儲存操作過程的串列 for(int i=0; i<m; i++) cin >> ops[i]; // 讀取操作過程 string s = ""; // 加密前的字串 string t; cin >> t; // 加密後的字串 for(int i=0; i<m; i++) { // 依序讀取操作過程 int num = 0; // 操作過程中 1 的數量 string op = ops[m-i-1]; // 第 m-i-1 個操作過程 for(int j=0; j<n; j++) { // 倒序讀取 op 的內容,計算 op 中 1 的數量,同時將 t 還原為 s if (op[n-j-1] == '0') { // 如果是 0,取下 t 最後面的字母加到 s 的最前面 s = t[t.size()-1] + s; t = t.substr(0, t.size()-1); } else if (op[n-j-1] == '1') { // 如果是 1,取下 t 最後面的字母加到 s 的最後面 num++; s = s + t[t.size()-1]; t = t.substr(0, t.size()-1); } } if (num%2 == 1) { // 如果 1 的數量是奇數,將 s 分成兩半、前後交換 if (n%2 == 0) s = s.substr(n/2, n/2) + s.substr(0, n/2); else s = s.substr(n/2 + 1, n/2) + s[n/2] + s.substr(0, n/2); } if (i < m-1) { // 如果還有未讀取的操作過程,重設 s 和 t 的內容 t = s; s = ""; } } cout << s << endl; // 印出解碼後的字串 return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`