# 北商資管114年程式競賽初階組詳解 > 十校聯盟程式競賽 - 初階組 詳解 --- # 1. 出現最多次的字母 用字典去紀錄每個字母出現的次數。 > 要注意的是123這種輸入。 ```python= s = input() d = {} for i in range(len(s)): if s[i].isalpha(): d[s[i]] = d.get(s[i],0)+1 if not d: print(0) else: mx = max(d.values()) count = sum(1 for k in d if d[k] == mx) print(1 if count >= 2 else 0) ``` # 2. 判斷字串的趨勢 透過建表mapping的方式快速查詢。 ```python= s = input().strip() d = {} for i, c in enumerate('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'): d[c] = i li = [d[c] for c in s] if all(li[i] > li[i-1] for i in range(1, len(li))): print(1) elif all(li[i] < li[i-1] for i in range(1, len(li))): print(2) else: print(3) ``` # 3. 相同內容的子字串 透過線性搜索及分段儲存來記錄。 ```python= n = input() li = ['0'] for i in n: if i!=li[-1][-1]:li.append(i) else: li[-1] += i mx = max(len(i) for i in li) print(mx) ``` # 4. 反向放置長單字 透過tmp來將單詞放入li中,達成 **"只要不是英文字母都split掉"** 的想法。 ```python= s = input() li = [] tmp = '' for i in s: if i.isalpha():tmp+=i else: li.append(tmp) tmp = '' li2 = [i for i in li if len(i) >=4] print(' '.join(reversed(li2))) ``` # 5. Excel 的儲存格個數 水題。 ```python= a,b = input().split(", ") wei = abs(ord(a[0].upper()) - ord(b[0].upper()))+1 hei = abs(int(a[1:])-int(b[1:]))+1 print(wei*hei) ``` # 6. 3 迴文的個數 雙迴圈使i,j固定距離4(含)以上。 > 移動窗戶(sliding window)實作。 ```python= s = input() ans = set() for i in range(0,len(s)): for j in range(i+4,len(s)+1): if s[i:j]==s[i:j][::-1] and len(set(i for i in s[i:j]))>=3: ans.add(s[i:j]) print(len(ans)) ``` # 7. 安全渡河的乘船法 先將所有排列列出後,在刪去不符合規則的。 ```python= from itertools import permutations s = input().strip() d = {''.join(p) for p in permutations(s)} li = [p for p in d if 'cd' not in p and 'cr' not in p] print(len(li)) ``` # 8. 神秘的X與Y 用replace()替換掉X,Y就好。 用枚舉的方式,一一將所有可能性帶入。 > 執行時間有2秒,絕對夠。 ```python= n,ans = input().split("=") li = [] for x in range(1,101): for y in range(1,101): tmp = n.replace('X',str(x)) t = eval(tmp.replace('Y',str(y))) if str(int(t)) == ans: li.append((x,y)) print(len(li)) ``` # 9. 二進位的 bit 1 個數 水題 ```python= li = list(map(int,input().split(", "))) a = max(li)-min(li) def tB(n): ans = '' while n!=0: ans +=str(n%2) n//=2 return ans ans = sum(1 for i in tB(a) if i=='1') print(ans) ``` # 10. 沒有重覆字元的字串個數 用bool指標"ok"來記錄是否有找到相同字,如果有相同字,ok就變成false。 ```python= li = list(input().split(", ")) cnt = 0 for w in li: ok = True for i in range(1, len(w)): if w[i] == w[i - 1]: ok = False break if ok: cnt += 1 print(cnt) ``` # 11. Pascal’s Triangle 第 n 列 高一數學。 > 巴斯卡三角形的第 i 行第 j 個數字 = C(i, j) math套件的comb可以幫我們快速計算C(i取j) ```python= from math import comb n = int(input()) li = [] for i in range(n): li.append(list(str(comb(i, j)) for j in range(i + 1))) print(' '.join(li[-1])) ``` # 12. 羅⾺數字解析器 只要左邊的數比當前的數小,就減左邊的數。 ```python= s = input().strip().upper() d = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000} ans = 0 for i in range(len(s)-1): if d[s[i]] < d[s[i+1]]: ans -= d[s[i]] else: ans += d[s[i]] ans += d[s[-1]] print(ans) ``` # 13. 數字重排差值 陷阱題,你可能會以為用`import ppermutations`來做排列,再者最大最小值,但這樣 **一定會超時**。 > 題目說找最大跟最小,其實就是升幂跟降冪兩個排列。 ```python= n = input().strip() s = sorted(n) mn = int(''.join(s)) mx = int(''.join(s[::-1])) print(mx - mn) ``` # 14. 進位換算 一樣使用mapping的方式快速完成。 ```python= S,M,N = input().split() M = int(M) N = int(N) def T(N,R): ans = '' mapping = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' while N>0: ans += mapping[N%R] N//=R return ans[::-1] k = T(int(S, M), N) print(k) ``` # 15. 尋找最接近質數 def p()函式來檢查該數是否是質數。 > 因為題目說距離一樣的話先輸出小的,所以右邊(<)先檢查。 注意1**不是**質數。 :::success 檢查到n是不是質數只需要檢查到$\sqrt{n}$ ::: ```python= def p(x): if x < 2: return False if x == 2: return True if x % 2 == 0: return False for i in range(3, int(x**0.5) + 1, 2): if x % i == 0: return False return True n = int(input()) d = 1 while True: if p(n - d): print(n - d) break if p(n + d): print(n + d) break d += 1 ``` # 16. 系統異常⾏為偵測 全部讀取完,在開始進行判斷。 採用dict+list的資料結構儲存資料 ```python= n=int(input()) d={} for _ in range(n): a,t,i=input().split() h,m=map(int,t.split(':')) t=h*60+m if a not in d:d[a]=[] d[a].append((t,i)) r=[] for a,l in d.items(): for j in range(len(l)): for k in range(j+1,len(l)): if abs(l[j][0]-l[k][0])<=10 and l[j][1]!=l[k][1]: r.append(a) break else:continue break print('\n'.join(sorted(r))if r else'None') ``` # 17. 簡易凱薩加密 轉成ASCII碼再加上來就好。 ```python= shift = int(input()) s = input() res = '' for ch in s: if 'A' <= ch <= 'Z': res += chr((ord(ch) - 65 + shift) % 26 + 65) elif 'a' <= ch <= 'z': res += chr((ord(ch) - 97 + shift) % 26 + 97) else: res += ch print(res) ``` # 19. 組成相同長度的字串 ```python= li = list(len(i) for i in map(str,input().split(', '))) li.sort() r = len(li)//2 T = False for i in range(len(li)-r): s = sum(li[i:i+r]) if sum(li)-s == s: T = True print('1'if T else '0') ``` # 20. 求中位數 水到靠杯的題目。 ```python= li1 = list(map(int,input().split(', '))) li2 = list(map(int,input().split(', '))) li = sorted(li1+li2) print(li[len(li)//2]) ``` # 21. 反轉數字 分別將小數點左右的數字取出後做反轉即可。 ```python= s = input() t, left, right = s[0], s[1:s.index('.')], s[s.index('.')+1:] ans = ''.join(i for i in left if i.isnumeric() and i != '0')[::-1] ans2 = ''.join(i for i in right if i.isnumeric() and i != '0')[::-1] print(f'{t if t!="+" else ""}{ans}.{ans2}') ``` :::danger 小心`print(f'{t if t!='+' else ''}{ans}.{ans2}')`全用單引號的話會讓DOMjudge 出現編譯錯誤。 ::: # 22. 找出共同最長的字尾 取出一個做為target,其他人只要都=target就OK,若有一個不=target,就直接終止迴圈。 > 寫法一,快速 ```python= li = input().split(', ') s = min(len(i) for i in li) ans = '' for i in range(1, s + 1): tar = li[0][-i] if all(j[-i] == tar for j in li): ans = tar + ans else: break print(ans if ans else "*") ``` > 寫法二,直觀邏輯 ```python= li = input().split(', ') s = min(len(i) for i in li ) ans = '' c = -1 T = None for _ in range(s): tar = li[0][c] for i in range(1,len(li)): if li[i][c] == tar: T = True else: T = False break if T:ans= tar + ans else: break c-=1 print(ans if ans else "*") ``` # 23. 洗了n次的撲克牌 因為只要洗過的牌都會被放到後面,所以我們可以去比較,"元順序"中,哪些牌還保留著原有的順序沒動。 ``` ori = ['A','2','3','4','5','6','7','8','9','10','J','Q','K'] li = input().split(',') ``` 也就是說我只需要從ori中,取出一個從i開始的子陣列,如果這個子陣列中的每個元素,都已相對位置的方式出現在li中,則i就是被洗牌的次數。 :::success 簡單來說,就是找ori[i:] 這個子陣列是否按照順序(無按照絕對位置)出現在li中。 (就是比較ori[i:]是否是li的子陣列) ::: ```python= def sq(o, l): it = iter(l) return all(any(x == y for y in it) for x in o) ori = ['A','2','3','4','5','6','7','8','9','10','J','Q','K'] li = input().split(',') for i in range(13): if sq(ori[i:], li): print(i) break ``` :::spoiler 舉例說明 如果我今天從ori的2(value = 3)開始往後與li比較,如果3~K都在li裡面且按照遞增順序(也就是3~K),那就代表只有A,2這兩張牌有被洗牌,則ori的index2(剛剛取出3的位置)就是洗牌的張數!! ::: :::info **什麼是iter()??** 單向消耗的指標, 每次找成功一次,iterator 就會停在那個位置的「後面」。 所以下一個 x 會接著繼續往後找。 ::: :::spoiler 過程演示 | 步驟 | x (o中當前) | 內層 it 位置 | 比對過程 | 結果 | it 停在哪 | | ---- | -------- | -------- | -------------------------------------- | ------------ | ------------------- | | 1️⃣ | '8' | 開頭 (`4`) | 4≠8, 3≠8, 5≠8, 6≠8, 2≠8, 7≠8, **8=8✅** | `any()`→True | 停在 '8' 之後(目前指向 '9') | | 2️⃣ | '9' | 從 '9' 開始 | **9=9✅** | True | 停在 '9' 之後(指向 'A') | | 3️⃣ | '10' | 從 'A' 開始 | A≠10, **10=10✅** | True | 停在 '10' 之後(指向 'J') | | 4️⃣ | 'J' | 從 'J' 開始 | **J=J✅** | True | 停在 'J' 之後(指向 'Q') | | ✅ 結果 | — | — | 所有 x 都成功 | `all()`→True | — | ::: # 24. 縱列相同的文字 用消耗指標iter()來快速將題目的輸入分成3組。 ```python= s = input() k = len(s)//3 n = iter(s) li = [[next(n) for _ in range(k)] for _ in range(3)] ans = [] for i in range(k): if li[0][i] == li[1][i] == li[2][i]: ans.append(int(li[0][i])) ans.sort() print(''.join(str(i) for i in set(ans)) if ans else 'N') ``` # 25. 重組成最大數字 題目有點小陷阱,不是只按照", "來做分割就好,為了避免有些測資有空白,有些沒有,建議疑慮: ```python n = input().replace(' ', '').split(',') ``` ```python= from functools import cmp_to_key def f(x, y): if x + y > y + x:# 這裡是指字串的相加 return -1 elif x + y < y + x: return 1 else: return 0 n = input().replace(' ', '').split(',') n.sort(key=cmp_to_key(f)) print(''.join(n)) ``` :::info **`from functools import cmp_to_key`** 是一個可以讓你自訂排序規則的function。 ::: :::spoiler 比較規則 -1 → 代表 **左**邊在**右**邊**前面** 1 → 代表 **左**邊在**右**邊**後面** 0 → 代表 兩者相等(順序不變) ::: # 26. 最長的連續序列 ```python= li = sorted(map(int, input().split(','))) tmp = ans = 1 for i in range(1, len(li)): if li[i] - li[i - 1] == 1: tmp += 1 else: ans = max(ans, tmp) tmp = 1 print(max(ans, tmp)) ``` # 27. 最少的轉動次數 限制一個數在指定範圍n中,就是直接%n ```python= a,b = input().split(", ") ans = 0 for a,b in zip(a,b): a,b = int(a),int(b) ans += min((b - a) % 10, (a - b) % 10) print(ans) ``` :::info zip() 會把多個可迭代物件"一個一個對位打包成 tuple"的形式組合起來。 :::spoiler 範例 ```python= a = [1, 2, 3] b = [4, 5, 6] print(list(zip(a, b))) ``` ::: # 28. 最長的子序列 跟23題很像,都是要尋找子序列,但23題有給你ori[i:]讓你去找,現在沒了 > 演算寫法 ```python= import bisect li = list(map(int, input().replace(" ", "").split(","))) tails = [] for num in li: pos = bisect.bisect_left(tails, num) if pos == len(tails): tails.append(num) else: tails[pos] = num print(len(tails)) ``` :::success bisect.bisect_left(li, num) 具體做的事是: 在一個「已排序」的列表 li 中,找到「第一個大於等於 num」的索引位置。 ::: :::spoiler 演算過程 假設輸入: ``` li = [10, 9, 2, 5, 3, 7, 101, 18] ``` 我們維護一個空陣列: ``` tails = [] ``` | 處理值 | tails | bisect_left 結果 | 更新說明 | | --- | -------------- | -------------- | ------------------- | | 10 | [10] | - | 新增第一個數 | | 9 | [9] | idx=0 | 取代 tails[0]=9(更小尾巴) | | 2 | [2] | idx=0 | 取代 tails[0]=2 | | 5 | [2, 5] | idx=1 | 新增在尾端(長度 +1) | | 3 | [2, 3] | idx=1 | 取代 tails[1]=3 | | 7 | [2, 3, 7] | idx=2 | 新增尾端(長度 +1) | | 101 | [2, 3, 7, 101] | idx=3 | 新增尾端 | | 18 | [2, 3, 7, 18] | idx=3 | 取代 tails[3]=18 | 最後 tails = [2, 3, 7, 18] LIS 長度 = len(tails) = 4 ::: > 直觀寫法 時間複雜度 ≈ O(n²)。 ```python= li = list(map(int,input().replace(" ", "").split(","))) sli = sorted(li) ans = [] for i in sli: idx = li.index(i) tmp = [0] for j in range(idx,len(li)): if li[j]>tmp[-1]:tmp.append(li[j]) ans.append(len(tmp)-1) print(max(ans)) ``` # 29. 奇幻數 用雙重迴圈去跑就夠快了。 ```python= n = input() for i in range(len(n)): for j in range(i+1,len(n)): cop = list(n) cop[i],cop[j] = n[j],n[i] val = int(''.join(cop)) if val % 29 == 0: print(1) break else: continue break else: print(0) ``` --- :::info 趁機宣傳一下我自己的個人網站跟Youtube頻道 !! **[個人網站](https://hyc.eshachem.com/) | [Youtube頻道](https://www.youtube.com/@Hy.C)** ::: @2025 Hy.C 陳毓 > Copyright ©Hy.C 陳毓 CC BY-NC-SA 4.0 | 禁止商業用途 | 轉載標記出處 | 改編作品必須在相同條款下分享。