[Leetcode解題] 2055. Plates Between Candles
6 December 2024
medium
presum
題目
有一張長桌,上面擺了一排盤子和蠟燭。用一個由 '*'
(代表盤子)和 '|'
(代表蠟燭)組成的字串 s
來描述桌上的擺設。另外會給你一組查詢 queries
,裡面每筆查詢是 [left, right]
,代表要檢查子字串 s[left...right]
之間的盤子數量。
條件是,這些盤子必須同時有蠟燭在左邊和右邊才算數。
比如說:
s = "||**||**|*"
, 查詢[3, 8]
的子字串是"*||**|"
,只有兩個盤子符合條件,因為兩邊都有蠟燭。
解題思路
這題其實就是要用一些巧妙的預處理來加速查詢。因為如果每次都直接遍歷子字串去數盤子會太慢,所以我們可以先計算出一些有用的資訊,像這樣:
- 計算累積盤子數量:
- 我們用一個陣列記錄每個位置之前的盤子數量(盤子數量的preSum),這樣要算某段範圍內的盤子數就可以直接用減法搞定。
- 找到最近的蠟燭:
- 我們要知道每個位置的左右最近蠟燭在哪,這樣才能確定盤子的有效範圍(盤子必須在蠟燭之間才算數)。
- 用兩次遍歷就可以完成,一次從左到右,一次從右到左。
- 處理查詢:
- 每個查詢只要用上面預處理的資訊,就能快速算出答案。
- 每次查詢都找出蠟燭的範圍,再用 盤子數量的preSum 算出範圍內的盤子數量。
Python 實作
class Solution:
def platesBetweenCandles(self, s: str, queries: List[List[int]]) -> List[int]:
n = len(s)
# Step 1: Precompute cumulative plate counts
plate_count = [0] * n
count = 0
for i, char in enumerate(s):
if char == '*':
count += 1
plate_count[i] = count
# Step 2: Find nearest left and right candles
left_candle = [-1] * n
right_candle = [-1] * n
nearest = -1
for i in range(n):
if s[i] == '|':
nearest = i
left_candle[i] = nearest
nearest = -1
for i in range(n - 1, -1, -1):
if s[i] == '|':
nearest = i
right_candle[i] = nearest
# Step 3: Process each query
result = []
for left, right in queries:
l = right_candle[left]
r = left_candle[right]
if l != -1 and r != -1 and l < r:
result.append(plate_count[r] - plate_count[l])
else:
result.append(0)
return result
C++ 實作
class Solution {
public:
vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
int n = s.size();
// Step 1: Precompute cumulative plate counts
vector<int> plateCount(n, 0);
int count = 0;
for (int i = 0; i < n; ++i) {
if (s[i] == '*') {
count++;
}
plateCount[i] = count;
}
// Step 2: Find nearest left and right candles
vector<int> leftCandle(n, -1), rightCandle(n, -1);
int nearest = -1;
// Compute left nearest candle
for (int i = 0; i < n; ++i) {
if (s[i] == '|') {
nearest = i;
}
leftCandle[i] = nearest;
}
// Compute right nearest candle
nearest = -1;
for (int i = n - 1; i >= 0; --i) {
if (s[i] == '|') {
nearest = i;
}
rightCandle[i] = nearest;
}
// Step 3: Process each query
vector<int> result;
for (const auto& query : queries) {
int left = query[0];
int right = query[1];
int l = rightCandle[left]; // First candle on the right of `left`
int r = leftCandle[right]; // Last candle on the left of `right`
if (l != -1 && r != -1 && l < r) {
result.push_back(plateCount[r] - plateCount[l]);
} else {
result.push_back(0);
}
}
return result;
}
};
時間複雜度
- 預處理階段是 $O(n)$
- 查詢階段每筆是 $O(1)$,所以總共是 $O(m)$。
- 整體時間複雜度是 $O(n + m)$。