【数组】【二分+前缀和】1、搜索插入位置+2、在排序数组中查找元素的第一个和最后一个位置+3、蜡烛之间的盘子+4、找出数组中的第一个回文字符串+5、构建回文串检测

今天继续做数组,打算今后,每天五道题的进度,目前是每天2道简单+3道中等
打算今天把数组主要的思路搞完,就开始下一个分类。
之后就是边学习新的,边练习旧的。

1、搜索插入位置(难度:简单)

在这里插入图片描述
该题对应力扣网址

AC代码

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int low=0,high=nums.size()-1;
        int mid = low+(high-low)/2;
        while(low<=high){
            if(nums[mid]>target){
                high=mid-1;
                mid = low+(high-low)/2;
            }
            else if(nums[mid]<target){
                low=mid+1;
                mid = low+(high-low)/2;
            }
            else{
                return mid;
            }
        }
        return low+(high-low)/2;

    }
};

看到这个时间复杂度,就想起来二分法,思路比较简单,记得学数据机构的时候学过,需要注意的就是这个写法mid = low+(high-low)/2;最好用这个,因为mid = (high+low)/2;如果数据太大的话加法容易溢出

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int length = nums.size();
        int low=0,high=length-1;
        int left,right;
        int mid = low+(high-low)/2;
        if(length==0){
            return {-1,-1};
        }
        while(low<=high){
            if(nums[mid]>target){
                high=mid-1;
                mid = low+(high-low)/2;
            }
            else if(nums[mid]<target){
                low=mid+1;
                mid = low+(high-low)/2;
            }
            //nums[mid]=target
            else{
                left = mid;
                right = mid;
                if(left!=0){
                    while(left!=0 && nums[left]==nums[left-1]){
                        left--;
                    }
                }
                if(right!=length-1){
                    while(right!=length-1 && nums[right]==nums[right+1]){
                        right++;
                    }
                }
                return {left,right};
            }
        }
        return {-1,-1};
    }
};

2、在排序数组中查找元素的第一个和最后一个位置(难度:中等)

在这里插入图片描述
该题对应力扣网址

AC代码

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int length = nums.size();
        int low=0,high=length-1;
        int left,right;
        int mid = low+(high-low)/2;
        if(length==0){
            return {-1,-1};
        }
        while(low<=high){
            if(nums[mid]>target){
                high=mid-1;
                mid = low+(high-low)/2;
            }
            else if(nums[mid]<target){
                low=mid+1;
                mid = low+(high-low)/2;
            }
            //nums[mid]=target
            else{
                left = mid;
                right = mid;
                if(left!=0){
                    while(left!=0 && nums[left]==nums[left-1]){
                        left--;
                    }
                }
                if(right!=length-1){
                    while(right!=length-1 && nums[right]==nums[right+1]){
                        right++;
                    }
                }
                return {left,right};
            }
        }
        return {-1,-1};
    }
};

没看题解怎么做的,就还是用的二分法,就在找到和target相等元素之后,又使用了left和right这一对新指针,left往左,right往右,和之前的三数之和里找重复元素的思路一样(双指针的思路算是应用的淋漓尽致的了)(够够的) ,注意一下两边边界问题就OK了。(虽然说是中等题,但感觉和简单的难度也差不多)

3、蜡烛之间的盘子(难度:中等)

在这里插入图片描述
该题对应力扣网址

超时了

class Solution {
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
        int low,high,count;
        int length=queries.size();
        int i,j,k;
        vector <int> ans;
        for(i=0;i<length;i++){
            count=0;
            low=queries[i][0];
            high=queries[i][1];
            while(s[low]!='|'){
                low++;
            }
            while(s[high]!='|'){
                high--;
            }
            if(low>=high){
                ans.push_back(0);
                continue;
            }
            for(j=low;j<=high;j++){
                if(s[j]=='*'){
                    count++;
                }
            }
            ans.push_back(count);
        }
        return ans;
    }
};

在这里插入图片描述

AC代码

原本以为自己之前的代码时间复杂度也可以,不过还是超时了,用的还是双指针的思想,不过,确实我有些思维固化了,只想着先获取边界,接着在每次用双指针从两边分别获取,缩小区间范围。这样相当于每次循环里都要再嵌套一个循环,如果情况比较坏,需要遍历整个字符串,时间复杂度就上去了。
看了下官方的题解,看到说有个预处理,当时只看了对*号做前缀和,觉得自己会了,后面就没再看。等到自己写的时候,还是乱七八糟小毛病一堆以及左右指针不知道该怎么改,不过最后终于是能够模仿着预处理的思路,想起来左右指针怎么来处理。(还是挺有成就感的)

class Solution {
public:
    vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {
        int low,high,count,count1,count2;
        int length=queries.size();
        int n = s.size();
        int i,j1,j2,k;
        vector <int> ans;
        vector <int> sum_star(n,0);
        vector <int> left(n,0);
        vector <int> right(n,0);
        count=0;
        count1=0;
        count2=n-1;
        //对某个下标之前的星号个数做统计(*的前缀和)
        for(k=0;k<n;k++){
            if(s[k]=='*'){
                count++;
            }
            sum_star[k]=count;
        }
        //指针从左往右,确定右边界
        for(j1=0;j1<n;j1++){
            if(s[j1]=='|'){
                count1=j1;
            }
            left[j1]=count1;
        }
        //指针从右往左,确定左边界
        for(j2=n-1;j2>=0;j2--){
            if(s[j2]=='|'){
                count2=j2;
            }
            right[j2]=count2;
        }
        for(i=0;i<length;i++){
            count=0;
            low=queries[i][0];
            high=queries[i][1];
            ans1=sum_star[high]-sum_star[low];
            ans2=high-low;
            //区间里只有一个蜡烛或没有蜡烛
            if(left[high]<=right[low]){
                ans.push_back(0);
                continue;
            }
            ans.push_back(sum_star[left[high]]-sum_star[right[low]]);
        }
        return ans;
    }
};

4、找出数组中的第一个回文字符串(难度:简单)

在这里插入图片描述
该题对应力扣网址

AC代码

思路比较简单,不多说了,上代码

class Solution {
public:
    string firstPalindrome(vector<string>& words) {
        int i,left,right;
        int n=words.size();
        string str;
        string ans="";
        for(i=0;i<n;i++){
            left=0;
            right=words[i].length()-1;
            while(left<right && words[i][left]==words[i][right]){
                left++;
                right--;
            }
            if(words[i][left]!=words[i][right]){
                continue;
            }
            //奇数个和偶数个
            if(left==right || left==right+1){
                return words[i];
            }

        }
        return ans;
    }
};

5、构建回文串检测(难度:中等)

在这里插入图片描述
该题对应力扣网址

解答错误

class Solution {
public:
    vector<bool> canMakePaliQueries(string s, vector<vector<int>>& queries) {
        vector <bool> ans;
        int i,left,right,k,count;
        int n=queries.size();
        string str;
        for(i=0;i<n;i++){
            left=queries[i][0];
            right=queries[i][1];
            k=queries[i][2];
            count=0;
            while(left<right && s[left]==s[right]){
                left++;
                right--;
            }
            //原本不是回文
            //看通过替换能否变成回文
            if(s[left]!=s[right]){
                if(k==0){
                    ans.push_back(false);
                }
                else{
                    while(left<right){
                        if(s[left]!=s[right]){
                            count++;
                        }
                        left++;
                        right--;
                    }
                    if(count>k){
                        ans.push_back(false);
                    }
                    else{
                        ans.push_back(true);
                    }
                }
                continue;
            }
            //奇数个和偶数个
            if(left==right || left==right+1){
                ans.push_back(true);
            }

        }
        return ans;
    }
};

在这里插入图片描述
当我看到这个错误案例的时候,我的小脑萎缩了一下。自信满满的 我打开评论区,看是不是样例给错了,结果。。

这题居然还可以重新排列,人家还特地加粗了,愣是没看着。。
我心想,这不完蛋,思路都得重来
确实作为中等题,不该这么简单的
以后要仔细审题啊友友们
看到题解说的方法,标题是,前缀和+位运算,想想也是哈,毕竟可以重新排列,都不在乎顺序了,前缀和是我目前学到的最好的方法了。
于是,我就没看题解代码,想自己敲一下,看能不能AC。
## AC代码

又超时了

前缀异或的方法没想起来怎么写,还是用的自己的笨方法,又超时了,哎呀,扶额,这俩测试样例,我真的。。。
在这里插入图片描述

class Solution {
public:
    vector<bool> canMakePaliQueries(string s, vector<vector<int>>& queries) {
        vector <bool> ans;
        // vector <int> alphabet={a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z};
        //小写英文字母ascii码为97-122
        vector <int> alphabet(123,0);
        int i,left,right,j,k,k1,count;
        int n=queries.size();
        string str;
        for(i=0;i<n;i++){
            alphabet.assign(123, 0);
            left=queries[i][0];
            right=queries[i][1];
            k1=queries[i][2];
            count=0;
            for(k=left;k<=right;k++){
                alphabet[s[k]]++;
            }
            for(j=97;j<=122;j++){
                if(alphabet[j]%2!=0){
                    //区间内单个字母的个数
                    count++;
                }
            }
            //区间内字母个数是偶数个
            if((right-left+1)%2==0){
                if(k1>=ceil(count/2)){

                    ans.push_back(true);
                }
                else{
                    ans.push_back(false);
                }
            }
            //区间内字母个数是奇数个
            if((right-left+1)%2!=0){
                if(k1>=ceil((count-1)/2)){
                    ans.push_back(true);
                }
                else{
                    ans.push_back(false);
                }
            }
        }
        return ans;
    }
};

AC代码

依旧是没看题解的方法,用的自己的笨方法,这已经算是暴力枚举了吧。。
主要思路就是,针对题目里说的字符串里都是小写英文字母,那么就仿照3、蜡烛之间的盘子这道题的做法,同样是给定区间,同样是计算每个字母的前缀和,不过相比第3题的一维数组,我这里就得用二维,并且字母在前字符串下标在后[字母][下标],反过来我试了,发现到后面的时候时间复杂度降不下来。用这个二维数组记录每个字符出现的次数。后面不说了,和蜡烛那个题思路差不多。

总结下来就是这些不考虑顺序的数组还是挺适合用前缀和的。

class Solution {
public:
    vector<bool> canMakePaliQueries(string s, vector<vector<int>>& queries) {
        vector <bool> ans;
        //{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z};
        //小写英文字母ascii码为97-122
        // vector <vector<int>> alphabet(s.size(),123,0);
        int n1=s.size();
        vector<vector<int>> alphabet(123, vector<int>(n1, 0));
        int i,i1,left,right,j,k,k1,count;
        int n=queries.size();
        string str;

        for(i1=0;i1<n1;i1++){
            if(i1>0){
                alphabet['a'][i1]=alphabet['a'][i1-1];
                alphabet['b'][i1]=alphabet['b'][i1-1];
                alphabet['c'][i1]=alphabet['c'][i1-1];
                alphabet['d'][i1]=alphabet['d'][i1-1];
                alphabet['e'][i1]=alphabet['e'][i1-1];
                alphabet['f'][i1]=alphabet['f'][i1-1];
                alphabet['g'][i1]=alphabet['g'][i1-1];
                alphabet['h'][i1]=alphabet['h'][i1-1];
                alphabet['i'][i1]=alphabet['i'][i1-1];
                alphabet['j'][i1]=alphabet['j'][i1-1];
                alphabet['k'][i1]=alphabet['k'][i1-1];
                alphabet['l'][i1]=alphabet['l'][i1-1];
                alphabet['m'][i1]=alphabet['m'][i1-1];
                alphabet['n'][i1]=alphabet['n'][i1-1];
                alphabet['o'][i1]=alphabet['o'][i1-1];
                alphabet['p'][i1]=alphabet['p'][i1-1];
                alphabet['q'][i1]=alphabet['q'][i1-1];
                alphabet['r'][i1]=alphabet['r'][i1-1];
                alphabet['s'][i1]=alphabet['s'][i1-1];
                alphabet['t'][i1]=alphabet['t'][i1-1];
                alphabet['u'][i1]=alphabet['u'][i1-1];
                alphabet['v'][i1]=alphabet['v'][i1-1];
                alphabet['w'][i1]=alphabet['w'][i1-1];
                alphabet['x'][i1]=alphabet['x'][i1-1];
                alphabet['y'][i1]=alphabet['y'][i1-1];
                alphabet['z'][i1]=alphabet['z'][i1-1];
            }
            alphabet[s[i1]][i1]++;
        }

        for(i=0;i<n;i++){
            left=queries[i][0];
            right=queries[i][1];
            k1=queries[i][2];
            count=0;

            for(k=97;k<=122;k++){
                if(left!=0){
                    if((alphabet[k][right]-alphabet[k][left-1])%2!=0){
                        count++;
                    }  
                }
                else{
                    if((alphabet[k][right]-0)%2!=0){
                        count++;
                    }  
                }

            }
            
            //区间内字母个数是偶数个
            if((right-left+1)%2==0){
                if(k1>=ceil(count/2)){
                    ans.push_back(true);
                }
                else{
                    ans.push_back(false);
                }
            }
            //区间内字母个数是奇数个
            if((right-left+1)%2!=0){
                if(k1>=ceil((count-1)/2)){
                    ans.push_back(true);
                }
                else{
                    ans.push_back(false);
                }
            }
        }
        return ans;
    }
};

下一篇博客再来学习题解的方法!一天5道完成了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

如果树上有叶子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值