# APCS實作題2019年6月第4題:美麗的彩帶 > 日期:2024年7月11日 > 作者:王一哲 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=e289) ## 題目 ### 問題描述 你有一條彩虹色的彩帶,但是因為它是異世界來的彩帶,所以可能不只七個顏色(異世界有可能有87色的彩虹),你有一個定義彩帶美麗度的標準,你定義一條彩帶的美麗度為它所擁有的長度為 $m$ 且有 $m$ 種顏色的子區間數量。現在你需要寫一個程式來計算一個字串的美麗度。 <br /> ### 輸入說明 第一行有兩個數字 $m$ 和 $n$,代表定義的子區間長度和彩帶長度。 第二行有 $n$ 個數字代表彩帶的顏色。 <br /> ### 輸出格式 輸出這條彩帶的美麗度。 <br /> ### 範例輸入 ``` 3 7 1 2 3 5 4 5 4 ``` ### 範例輸出 ``` 3 ``` <br /> ## Python 程式碼 ### 寫法1,配合 set 直接掃過去 很直接的作法,但是效率不好,第10筆測資超時。 ```python= m, n = map(int, input().split()) # 讀取子區間長度 m、彩帶長度 n cs = list(map(int, input().split())) # 讀取彩帶顏色 ans = 0 # 答案 for i in range(n-m+1): # 掃過一輪,i = 0 ~ n-m sub = cs[i:i+m] # 子區間 if len(set(sub)) == m: ans += 1 # 將 sub 轉成 set,計算顏色種類,如果等於 m 則 ans 加 1 print(ans) # 印出答案 ``` <br /><br /> ### 寫法2,固定窗口大小,只處理右端新增、左端出界的項目 第18筆測資費時最久,約為 0.4 s,使用記憶體最多約為 105.6 MB,通過測試。 ```python= from collections import Counter # 使用計數器 Counter m, n = map(int, input().split()) # 讀取子區間長度 m、彩帶長度 n cs = list(map(int, input().split())) # 讀取彩帶顏色 ans = 0 # 答案 cnt = Counter(cs[:m]) # 計數器,先處理 cs[0:m] if len(cnt) == m: ans += 1 # 如果有 m 種顏色,ans 加 1 for i in range(1, n-m+1): # 掃過剩下的彩帶 # 如果出界的彩帶於 cnt 中只有一段,從 cnt 中刪除;如果數量大於1則減1 if cnt[cs[i-1]] == 1: del cnt[cs[i-1]] elif cnt[cs[i-1]] > 1: cnt[cs[i-1]] -= 1 # 如果新增的彩帶不在 cnt 中,新增此段的顏色,數量為1;反之數量加1 if cs[i+m-1] not in cnt: cnt[cs[i+m-1]] = 1 else: cnt[cs[i+m-1]] += 1 # 如果 cnt 長度等於 m,ans 加1 if len(cnt) == m: ans += 1 print(ans) # 印出答案 ``` <br /><br /> ## C++ 程式碼 ### 寫法1,配合 set 直接掃過去 很直接的作法,但是效率不好,第5筆測資超時。 ```cpp= #include <iostream> #include <vector> #include <set> using namespace std; int main() { int m, n; cin >> m >> n; // 讀取子區間長度 m、彩帶長度 n vector<int> cs (n, 0); for(int i=0; i<n; i++) cin >> cs[i]; // 讀取彩帶顏色 int ans = 0; // 答案 for(int i=0; i<=n-m; i++) { // 掃過一輪,i = 0 ~ n-m set<int> sub (cs.begin()+i, cs.begin()+i+m); // 子區間 if (sub.size() == m) ans++; // 將 sub 轉成 set,計算顏色種類,如果等於 m 則 ans 加 1 } cout << ans << "\n"; // 印出答案 return 0; } ``` <br /><br /> ### 寫法2,固定窗口大小,只處理右端新增、左端出界的項目 最後4筆測資輸入的數值超大,上限為 $10^{150}$,如果用 int 作為 cnt 的 key 值會爆掉,要用 string 作為 key 值。費時最久約為 0.2 s,使用記憶體最多約為 64.5 MB,通過測試。 ```cpp= #include <iostream> #include <vector> #include <unordered_map> #include <string> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); // 加速,不然最後4筆測資會超時 int m, n; cin >> m >> n; // 讀取子區間長度 m、彩帶長度 n vector<string> cs (n); for(int i=0; i<n; i++) cin >> cs[i]; // 讀取彩帶顏色 int ans = 0, num = 0; // 答案 ans,子區間內顏色種類數量 num unordered_map<string, int> cnt; // 計數器 for(int i=0; i<m; i++) { // 先處理 cs[0:m] if (cnt[cs[i]] == 0) num++; // 如果原來 cnt 中 cs[i] 的數量為0,增加一種新的顏色,num 加1 cnt[cs[i]]++; // cnt 中對應的顏色數量加1 } if (num == m) ans++; for(int i=1; i<=n-m; i++) { // 掃過剩下的彩帶 // 如果出界的彩帶於 cnt 中原來只有一段,子區間中少一種顏色,num 減1 if (cnt[cs[i-1]] == 1) num--; cnt[cs[i-1]]--; // 如果新增的彩帶不在 cnt 中,新增此段的顏色,num 加1 if (cnt[cs[i+m-1]] == 0) num++; cnt[cs[i+m-1]]++; // 如果有 m 種顏色,ans 加 1 if (num == m) ans++; } cout << ans << "\n"; // 印出答案 return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`