区间统计查询(预处理+前缀和)

原题在这里

这题我思路完美,但是在实现的时候被疯狂卡边界问题(大晚上的脑子不清醒,一如既往的空想不打草稿地做题)。

概述题意:给定两种符号"*","|"组成的字符串,要求输出给定(数组)区间内"|"包含的"*"数量和。

输入:s = "**|**|***|", queries = [[2,5],[5,9]]
输出:[2,3]
解释:
- queries[0] 有两个盘子在蜡烛之间。
- queries[1] 有三个盘子在蜡烛之间。

下面是我的思路过程:

analyse:
        1.由于是区间查询,想到树状数组和倍增打表
        2.由于是两个蜡烛之间,所以考虑将直接将蜡烛的下标对标ans处理(前缀和),定义num[i]表示符号"|"的序号数值
            区间[l,r]
            处理成下标对应蜡烛数目,然后边缘收缩(左右边界蜡烛下标x,y)即是答案(y-x-(num[y]-num[x]))
            进一步优化边缘收缩
                将所有的蜡烛存入数组中,直接处理数组,二分搜索,时间复杂度就是线性了
           【这里我就直接开始上手了,然后因为二分搜索被疯狂卡边界】
                [还可以将蜡烛直接处理成边界](预处理)
下面是我前缀和+二分代码:
class Solution
{
    vector<int> vc;
    int find(int x)
    {
        int l = -1, r = vc.size();
        while (l + 1 < r)
        {
            int mid = (l + r) >> 1;
            if (vc[mid] <= x)
                l = mid;
            else
                r = mid;
        }
        return r;
    }

public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>> &queries)
    {
        int sum = 0, len1 = s.length(), len2 = queries.size(), l, r;
        vector<int> num(s.length()), ans;
        for (int i = 0; i < len1; ++i)
            if (s[i] == '|')
                num[i] = ++sum, vc.emplace_back(i);
        for (int i = 0; i < len2; ++i)
        {
            l = queries[i][0], r = queries[i][1];
            if (vc.size() > 0)
            {
                l = vc[min(find(l - 1), (int)vc.size() - 1)]; //第一个区间左侧蜡烛位置
                r = vc[max(find(r) - 1, 0)];                  //最后一个区间右侧蜡烛位置
            }
            else
                l = r;
            ans.emplace_back((r >= l) ? r - l - (num[r] - num[l]) : 0);
        }
        return ans;
    }
};
ugly

还有一点是,在传形参给函数的时候,如果参数是数组,多次调用函数也会大量消耗不应该的time。

然后是很标准的前缀和+预处理:

 

class Solution
{
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>> &queries)
    {
        int sum = 0, len1 = s.length(), len2 = queries.size(), l, r;
        vector<int> num(len1), left(len1), right(len1), ans;
        for (int i = 0, l = len1 - i - 1, r = i; i < len1; ++i)
        {
            if (s[i] == '|')
                num[i] = ++sum, r = i;
            if (s[len1 - i - 1] == '|')
                l = i;
            right[i] = r;           //右侧的边界蜡烛下标
            left[len1 - i - 1] = l; //左侧边界蜡烛下标
        }
        for (int i = 0; i < len2; ++i)
        {
            l = queries[i][0], r = queries[i][1];
            l = right[l], r = left[r];
            cout << "l==" << l << ", r==" << r << endl;
            ans.emplace_back((r >= l) ? r - l - (num[r] - num[l]) : 0);
        }
        return ans;
    }
};
beautiful

 

嗐,至少说分析已经很正确了,题解区也很多我这样的做法,但是还是不够完美。

posted @ 2022-03-08 22:53  Renhr  阅读(24)  评论(0)    收藏  举报