# APCS實作題2022年1月第3題:數位占卜 > 日期:2024年8月1日 > 作者:王一哲 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=h083) ## 題目 ### 問題描述 占卜籤筒有 $m$ 支籤,每一支籤為一個由英文小寫字母組成的字串。從籤筒內抽出兩支籤,若將這兩支籤上的字串 $S$ 和 $T$ 連接起來形成的字串可以將該字串拆成左右兩半並且內容一樣,則抽到聖筊代表神明同意,否則神明不同意或是沒回答。 例如抽出的兩支籤上的字串分別為 piep 和 ie,則相連接起來的字串為 piepie 可以拆分左右兩半為相同的字串 pie 和 pie,但抽出的兩支籤為 foo 和 bar 時則不滿足條件。 神奇的是,若抽到的兩支籤 $S$ 和 $T$ 為聖筊,則不管是將 $T$ 接在 $S$ 後面或是順序反過來接起來,都可以是聖筊,再次說明了這兩支籤有著某種神秘力量在祝福著抽到的幸運人。例如 piep 和 ie 不管是使用 piepie 或是 iepiep 都可以拆分成兩個一樣的字串。 詢問籤筒內這 $m$ 支籤,有幾個 pair 可以形成聖筊。相同的兩支籤組合計算一次即可。 <br /> ### 輸入說明 輸入一個正整數 $m$,接下來有 $m$ 的字串,每個字串長度最長為 $100$。 數字範圍 - $1 \leq m \leq 50000$ - 籤筒內所有字串均相異 子題配分 - 20%:$2 \leq m \leq 100$,字串長度只會是 10 或是 20。 - 20%:字串長度只會是 10 或是 20 - 60%:無額外限制 <br /> ### 輸出格式 輸出一個正整數,代表有幾個 pair 滿足條件。 <br /> ### 範例輸入1 ``` 3 a aba aaa ``` ### 範例輸出1 ``` 1 ``` ### 範例輸入2 ``` 5 abyyyab y yy yyy yyyy ``` ### 範例輸出2 ``` 3 ``` <br /><br /> ## Python 程式碼 ### 寫法1,暴力解,會超時 到第4筆測資時會超時。 ```python= m = int(input()) # 字串數量 words = [] # 所有的字串 for _ in range(m): words.append(input()) # 讀取所有的字串 ans = 0 # 答案 for i in range(m): # 依序産生 0 ~ m-1 s = words[i] # 字串 s for j in range(i+1, m): # 依序産生 i+1 ~ m-1 t = words[j] # 字串 t st = s+t # 組合 s+t n = len(st) # st 長度 if n&1: continue # 如果長度是奇數,不符合要求,找下一個 if st[:n//2] == st[n//2:]: ans += 1 # 如果符合要求,ans 加 1 print(ans) # 印出答案 ``` <br /><br /> ### 寫法2,利用 set 加速 解題關鍵在於第9 ~ 14行,若以 abyyyab 為例,執行第9 ~ 14行的結果依序為 ```python= j = 1, left = a, right = b j = 2, left = ab, right = ab, target = yyy j = 3, left = aby, right = yab ``` 費時最久約為 2.4 s,使用記憶體最多約為 12.7 MB,通過測試。 ```python= m = int(input()) # 字串數量 words = [] # 所有的字串 for _ in range(m): words.append(input()) # 讀取所有的字串 wordSet = set(words) # 儲存所有字串的 set,判斷 set 之中是否有 target 的速度較快 ans = 0 # 答案 for s in words: # 依序讀取字串存入 s n = len(s) # s 的長度 for j in range(1, n//2+1): # 分割子字串的索引值,最多切到正中央 left = s[:j] # 左側子字串 right = s[n-j:] # 左側子字串 if left == right: # 如果左、右子字串相同 target = s[j:n-j] # 剩下的部分是符合條件的目標字串 if target in wordSet: ans += 1 # 如果 target 在 wordSet 之中,ans 加 1 #if target in words: ans += 1 # 速度較慢,會超時 print(ans) # 印出答案 ``` <br /><br /> ## C++ 程式碼 ### 寫法1,暴力解,會超時 到第4筆測資時會超時。 ```cpp= #include <iostream> #include <vector> #include <string> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); // 加速 int m; cin >> m; // 字串數量 vector<string> words (m); // 所有的字串 for(int i=0; i<m; i++) cin >> words[i]; // 讀取所有的字串 long long ans = 0; // 答案,用 int 會溢位 for(int i=0; i<m; i++) { // 依序産生 0 ~ m-1 string s = words[i]; // 字串 s for(int j=i+1; j<m; j++) { // 依序産生 i+1 ~ m-1 string t = words[j]; // 字串 t string st = s+t; // 組合 s+t size_t n = st.size(); // st 長度 if (n&1) continue; // 如果長度是奇數,不符合要求,找下一個 if (st.substr(0, n/2) == st.substr(n/2, n/2)) ans++; // 如果符合要求,ans 加 1 } } cout << ans << "\n"; // 印出答案 return 0; } ``` <br /><br /> ### 寫法2,利用 set 加速 費時最久約為 0.8 s,使用記憶體最多約為 13.8 MB,通過測試。 ```cpp= #include <iostream> #include <vector> #include <string> #include <set> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); // 加速 int m; cin >> m; // 字串數量 vector<string> words (m); // 所有的字串 set<string> wordSet; // 儲存所有字串的集合 for(int i=0; i<m; i++) { // 讀取所有的字串 cin >> words[i]; wordSet.insert(words[i]); } long long ans = 0; // 答案,用 int 會溢位 for(string s : words) { // 依序讀取字串 s size_t n = s.size(); // 字串 s 長度 for(size_t j=1; j<=n/2; j++) { // 依序産生 1 ~ n/2 string left = s.substr(0, j); // 左側子字串 string right = s.substr(n-j, j); // 右側子字串 if (left == right) { // 如果左、右子字串相等 string target = s.substr(j, n-2*j); // 剩下的部分是目標字串 ans += wordSet.count(target); // 如果 target 在 wordSet 之中 ans 加 1 } } } cout << ans << "\n"; // 印出答案 return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`