日期:2026年9月1日
ZeroJudge 題目連結:s215.紙膠帶 (Tape)
題目 pdf 檔連結:紙膠帶 (Tape)
解題想法
題目是多筆測資。每筆測資第一列是代表紙膠帶長度的整數 $N$,第二列有 26 個正整數代表每種動物(字母)數量上限,第三列是代表紙膠帶圖案的字串。題目規定一段連續的漂亮紙帶必須符合以下 2 個條件:
- 紙帶上正好有 3 種動物
- 紙帶上最多只能有 1 種動物數量超過上限
- 紙帶上沒有動物超過數量上限,以 3 種動物的最大數量計分。
- 紙帶上正好有 1 種物物超過數量上限,以超過上限的數量計分。
Python 程式碼
使用時間約為 0.8 s,記憶體約為 10.9 MB,通過測試。
def solve():
import sys
def get_tokens():
for line in sys.stdin:
for token in line.split():
yield token
tokens = get_tokens()
result = []
while True:
try:
N = int(next(tokens))
limits = [0] * 26
for i in range(26):
limits[i] = int(next(tokens))
s = next(tokens)
except StopIteration:
break
def get_score(max_exceed):
# 自訂函式,用滑動視窗取連續子字串分數,代入可以有幾個字母超標
left = 0 # 視窗左端點
imax = 0 # 最高分數
curr = set() # 視窗中的字母索引值
exceed = set() # 超標的字母
cnt = [0] * 26 # 視窗中的字母計數器
for right in range(N): # 右端點 0 ~ N-1
ri_idx = ord(s[right]) - ord('a') # 右端點字母索引值
curr.add(ri_idx) # ri_idx 加入 curr
cnt[ri_idx] += 1 # ri_idx 數量加 1
# 如果 ri_idx 超標,ri_idx 加入 exceed
if cnt[ri_idx] == limits[ri_idx] + 1: exceed.add(ri_idx)
# 左端點向右滑,直到視窗內字母種類等於 3 且超標數量等於 max_exceed
while left < right and (len(curr) > 3 or len(exceed) > max_exceed):
le_idx = ord(s[left]) - ord('a') # 左端點字母索引值
left += 1
cnt[le_idx] -= 1
# 如果 le_idx 降回數量上限,exceed 移除 le_idx
if cnt[le_idx] == limits[le_idx]: exceed.remove(le_idx)
# 如果 le_idx 降回數量歸零,curr 移除 le_idx
if cnt[le_idx] == 0: curr.remove(le_idx)
# 依照 max_exceed 計分
score = 0
if len(curr) == 3: # 有 3 種字母才有分數
if max_exceed == 1 and len(exceed) == 1: # 只有一種超標,這個字母數量是分數
score = cnt[list(exceed)[0]]
elif max_exceed == 0 and len(exceed) == 0: # 沒有字母超標,curr 之中 3 個字母數量最大值是分數
score = max(cnt[idx] for idx in curr)
# 更新最高分數
imax = max(imax, score)
return imax
# 用兩次滑動視窗找最高分
ans = max(get_score(0), get_score(1))
result.append(f"{ans:d}\n")
sys.stdout.write("".join(result))
if __name__ == "__main__":
solve()