# 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++`
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.