# 2055 - Plates Between Candles
###### tags: `leetcode.binarySearch` `leetcode.medium`
[題目連結](https://leetcode.com/problems/plates-between-candles)
## intuision
- 因為字串中只包含'|'跟'\*',所以可以直接這樣解:
- 對每個輸入query,找出距離query[0]最近,且比query[0] index還大的第一個'|',令他為**X**;以及找出距離query[1]最近,且比query[1] index還小的第一個'|',令他為**Y**
- e.g. s = " |\*|\*\*\*\|\|\*| " , query = [1,8],則
- **X** = 第2根candle, **Y** = 第4根candle
- 找出**X**跟**Y**之後,直接計算**X**跟**Y**之間的\*數就好
## implementation
- Brute force ($O(n^2)$, TLE)
- 對每個query,擷取出他的子字串sub_str(範圍在query[0]~query[1])
- 在sub_str計算在第一次碰到'|'之後到最後一次'|'之前的\*數
```C++
bool flag = 0;
int acc = 0;
int ret = 0;
int i=0;
for(i=0;i<sub_str.length() && sub_str[i] != '|'; i++){}
for(;i<sub_str.length();i++){
if(sub_str[i] == '|'){
ret += acc;
acc = 0;
}
else{
acc++;
}
}
return ret;
```
- 每個子字串長度達n,有n個query,所以複雜度$O(n^2)$
- Binary search ($O(n\;log\;n)$)
- 首先跑一遍string,記錄所有'|'的index到candle_array中
- 分別用query[0]跟query[1]的值去對candle_array跑binary search (規則同intuition),index即為intuition的**X**跟**Y**
- 中間的'\*'即為
```( (candle_array[Y] - candle_array[X]) - (Y-X) );```
> (Y-X) 的意涵是扣掉index之間,是"|"的數量
## 難點&心得
- 實作binary search的變種,該algorithm須滿足:
- 找出距離target value最近的index,**target value有可能不存在array中**
- 分成左值跟右值,左值要找>=target value的值;右值要找<= target value的值 (故需要分成兩個binary search function)
- 如果左值target value大於最大的index,回傳-1 (該query應該回傳0);右值亦然(target<最小的index)
- 基礎binary search,基本array長度應該是多少,變種後又應該是多少
- array.size() < 多少,應該個別處理,而非使用binary search?
- 不用特別做special case(n<3特別處理等等),BS可以直接用iterative的方式去做,當初的條件設錯...
- C++有提供upper_bound跟lower_bound兩種algorithm,分別回傳vector中「大於或等於」val的「最小值」的位置、vector中「大於」val的「最小值」的位置
- 用自己的BS(註解掉的地方)會TLE,用C++的library就AC了...(汗)
## 程式碼
```cpp=
class Solution {
public:
vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
vector<int> candles;
vector<int> sol;
for(int i=0; i<s.length(); i++){
if(s[i]=='|'){
candles.push_back(i);
}
}
for(auto query:queries){
int i = lower_bound(candles.begin(), candles.end(), query[0]) - candles.begin();
int j = upper_bound(candles.begin(), candles.end(), query[1]) - candles.begin() - 1;
sol.push_back(i < j ? (candles[j] - candles[i]) - (j - i) : 0);
// int leftCandleIndex = findLeftCandle(query[0], candles);
// int rightCandleIndex = findRightCandle(query[1], candles);
// if(leftCandleIndex == -1 || rightCandleIndex == -1 || leftCandleIndex >= rightCandleIndex){
// sol.push_back(0);
// }
// else{
// sol.push_back( candles[rightCandleIndex]-candles[leftCandleIndex]-(rightCandleIndex-leftCandleIndex) );
// }
}
return sol;
}
private:
int findLeftCandle(int target, vector<int> candles){
int left = 0, right = candles.size()-1;
while(left<=right){
int mid = (left+right)/2;
if(candles[mid] > target){
right = mid-1;
}
else if(candles[mid] < target){
left = mid+1;
}
else{
return mid;
}
}
if(right<0){
return 0;
}
if(left>candles.size()-1){
return -1;
}
return left; //left is the bigger one
}
int findRightCandle(int target, vector<int> candles){
int left = 0, right = candles.size()-1;
while(left<=right){
int mid = (left+right)/2;
if(candles[mid] > target){
right = mid-1;
}
else if(candles[mid] < target){
left = mid+1;
}
else{
return mid;
}
}
if(right<0){
return -1;
}
if(left>candles.size()-1){
return candles.size()-1;
}
return right; //right is the smaller one
}
};
```