每日一题——[蜡烛之间的盘子]

143 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述:

给你一个长桌子,桌子上盘子和蜡烛排成一列。给你一个下标从 0 开始的字符串 s ,它只包含字符 “ * ” 和 '|' ,其中 ' * ' 表示一个 盘子 ,'|' 表示一支 蜡烛 。

同时给你一个下标从 0 开始的二维整数数组queries ,其中 queries[i] = [lefti, righti]表示 子字符串 s[lefti,righti] (包含左右端点的字符)。对于每个查询,你需要找到 子字符串中 在 两支蜡烛之间 的盘子的 数目 。如果一个盘子在子字符串中 左边和右边都至少有一支蜡烛,那么这个盘子满足在两支蜡烛之间 。

比方说,s = "||* * ||** | " ,查询 [3, 8] ,表示的是子字符串 "*||**|" 。子字符串中在两支蜡烛之间的盘子数目为 2 ,子字符串中右边两个盘子在它们左边和右边都至少有一支蜡烛。 请你返回一个整数数组answer ,其中 answer[i]是第i 个查询的答案。

示例 1:

image.png

输入:s = " | |***|", queries = [[2,5],[5,9]] 输出:[2,3] 解释:

  • queries[0] 有两个盘子在蜡烛之间。
  • queries[1] 有三个盘子在蜡烛之间。

示例 2:

image.png

输入:s = " | | ***** | || | ", queries = [[1,17],[4,5],[14,17],[5,11],[15,16]] 输出:[9,0,0,0,0] 解释:

  • queries[0] 有 9 个盘子在蜡烛之间。
  • 另一个查询没有盘子在蜡烛之间。

提示:

3 <= s.length <= 105 s 只包含字符 '*' 和 '|' 。 1 <= queries.length <= 105 queries[i].length == 2 0 <= lefti <= righti < s.length

思路分析:

暴力思路

看到这道题我首先想到用暴力的思路去解决:遍历字符串,把每一个字符挨个入栈,然后当栈不为空时,把元素不断出栈,当遇到‘|’时,开始统计‘’的数量直到栈顶元素为下一个‘|’,为了预防只有一个‘|’的情况,我们把每次统计的 * 个数存下,如果没有下一个’|‘了,我们减去这次加的数量;

暴力代码:

class Solution {
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
        int query_num = queries.size();
        
        vector<int> hh;
        for(int i = 0;i < query_num;i ++)
        {
            stack<char> s1;
            vector<int> qq = queries[i];
            // 开始处理字符串
            for(int j = qq[0];j <= qq[1];j ++)
            {
                s1.push(s[j]);
            }
            // 开始处理栈
            int result = 0;
           while(!s1.empty())
            {
                //cout << s1.size() << endl;
                if( s1.top() == '|')
                {
                    s1.pop();
                    int res = 0;
                    while(!s1.empty() && s1.top()!='|')
                    {   
                        if(s1.top() == '*'){
                            
                            res++;
                            result ++;
                            s1.pop();
                        }
                        //cout << "res = " << res << endl;;
                    }
                    if(s1.empty()) result -= res;
​
                }else if(!s1.empty()){
                    s1.pop();
                }
            }
            hh.push_back(result);
        }
        return hh;
    }
};

注意:这样时间和空间复杂度都很高,不能通过答案;

算法优化

我们这里想到用前缀和 + 预处理进行优化。我们先把盘子的数量用数组sum[ N ]进行前缀和处理,那么在计算两个蜡烛之间盘子的数量时,只需要知道盘子左边和右边蜡烛的位置,假设左边蜡烛的位置为x,右边蜡烛的位置为y,那么两个位置之间的盘子数量即为sum[y] - sum[x]

这样操作可以把计算盘子数量和寻找蜡烛的两个操作的时间复杂度降为O(1)。

具体代码实现:

class Solution {
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
        int length = s.length();
        // 前缀和处理
        int N = 100010;
        int sum[N], num = 0;
        for(int i = 0;i < length;i ++)
        {
            if(s[i] == '*')
            {
                num ++;
            }
            sum[i] = num;
        }
        int left[N], right[N], l = -1,r = -1;
        // 寻找蜡烛的位置
        for(int i = 0;i < length;i ++)
        {
            if(s[i] == '|')
            {
                l = i;
            }
            left[i] = l;
        }
        // 寻找蜡烛的位置
        for(int i = length - 1;i >= 0;i --)
        {
            if(s[i] == '|')
            {
                r = i;
            }
            right[i] = r;
        }
        
        int si = queries.size();
        vector<int> aa;
        for(int i = 0;i < si;i ++)
        {
            auto ans = queries[i];
            int x = right[ans[0]], y =left[ans[1]];
            int result = (x == -1 ||y == -1 || x >= y?0:sum[y] - sum[x]);
            // 把结果存入vector数组中
            aa.push_back(result);
        }
        return aa;
    }
};

\