【每日一题】 2024年1月汇编

news2024/11/15 1:31:49

🔥博客主页: A_SHOWY
🎥系列专栏:力扣刷题总结录 数据结构  云计算  数字图像处理  力扣每日一题_

【1.4】2397.被列覆盖的最多行数

2397. 被列覆盖的最多行数icon-default.png?t=N7T8https://leetcode.cn/problems/maximum-rows-covered-by-columns/

这个题目真的太巧妙了,运用这种位运算来模拟这个数组,然后,将数组用位运算以后的二进制和存储,然后,用位运算设置limit,给while循环,1个数不够的还得continue了,最后按位与,整体结构都非常巧妙。

  1. 第一部分,存储每一行的二进制表示位,用一个数组把一个转为二进制存起来。
     vector<int> mask(m,0);
            //存储每一行的二进制表示位
            for(int i = 0; i < m; i++){
                for(int j = 0; j < n; j++){
                    mask[i] += matrix[i][j] << (n - j -1);
                }
            }
  2.  在判断的时候,用limit卡住界限,维护列变换的区间
     while((++cur) < limit){
                if(__builtin_popcount(cur) != numSelect) {
                    continue;
                }
  3. 用了一个内置函数关于一个二进制中1 的个数的函数,还有0的个数的函数
    __builtin_popcount(cur) //1的个数
    __builtin_ctz(cur) //0的个数,count trailing zero
  4. 最后把每一行遍历一遍,与变换后的列的第一行按位与,看看能不能覆盖这一行的所有1
class Solution {
public:
    int maximumRows(vector<vector<int>>& matrix, int numSelect) {
        if(numSelect >= matrix[0].size()) return matrix.size();
        int m = matrix.size();
        int n = matrix[0].size();
        vector<int> mask(m,0);
        //存储每一行的二进制表示位
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                mask[i] += matrix[i][j] << (n - j -1);
            }
        }
        int res = 0;//记录最大行数
        int cur = 0;//最大列数
        int limit = (1 << n);
        while((++cur) < limit){
            if(__builtin_popcount(cur) != numSelect) {
                continue;
            }
            int t = 0;
            for(int j = 0; j < m; j++){//遍历一遍按位与
                if((mask[j] & cur) == mask[j]){
                    t++;
                }
            }
            res = max(res,t);
        }
    return res;
    }
};

【1.14】83.删除排序链表中的重复元素

83. 删除排序链表中的重复元素icon-default.png?t=N7T8https://leetcode.cn/problems/remove-duplicates-from-sorted-list/

很久没有做到链表题目,但是这道确实简单,只需要注意一下while循环中除了cur非空,还要兼顾cur -> next非空。重复时,就让指针指向next -> next

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
    ListNode* cur = head;
    while(cur != NULL && cur ->next != NULL)
    {
        if(cur -> val == cur -> next -> val) {
            cur -> next = cur -> next -> next;
        }
        else cur = cur -> next;
    }
    return head;
    }
};

【1.15】82.删除排序链表中的重复Ⅱ

82. 删除排序链表中的重复元素 IIicon-default.png?t=N7T8https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/

与83题删除排序链表中元素的主要区别为此题要求将出现过的重复元素的所有元素均删除例如1,2,2,2将所有的2删除。需要引入头结点,因为头部可能也会被删除,统一操作。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
    ListNode*  dummyhead =  new ListNode(0); 
    dummyhead -> next =head;
    ListNode* cur = dummyhead;   
   while(cur -> next && cur-> next -> next){
       if(cur -> next-> val == cur -> next -> next -> val)
       {
           int x = cur -> next -> val;
           while(cur -> next && cur -> next -> val == x){//直到cur -> next 为空节点或者cur -> next 不为x
               cur -> next = cur -> next -> next;
           }
       }
       else cur = cur -> next;
   }
   head = dummyhead -> next;
   delete dummyhead;
   return head;
    }
};

 那么本题的总体思路也是比较简单,先设置一个虚拟头结点dummyhead,其具体步骤为

 ListNode*  dummyhead =  new ListNode(0); 
    dummyhead -> next =head;
    ListNode* cur = dummyhead;   

如果节点的下一个等于其下一个的下一个,就说明重复,把重复的数字记下来,统一删除。while中判定条件是删除直到cur的下一个是空节点或者粗人的下一个的值不是x。最后再删除头结点。

【1.17】2744.最大字符串配对数目

2744. 最大字符串配对数目icon-default.png?t=N7T8https://leetcode.cn/problems/find-maximum-number-of-string-pairs/

该题目如果暴力做,二重循环思路比较简单,但是也可以用哈希法解决。

法一:暴力

class Solution {
public:
    int maximumNumberOfStringPairs(vector<string>& words) {
        int ans = 0;
        for(int i = 0; i < words.size();++i){
            for(int j = i + 1; j < words.size(); ++j){
                if(words[i][0] == words[j][1] && words[i][1] == words[j][0]) ans++;
            }
        }
        return ans;
    }
};

 法二:哈希

1.set.count 表示统计元素出现的次数不为0

2.用哈希值来存,题目限制是一个两位的字符串,同时都是小写字母,所以可以用第一位*100+第二位作为哈希值来存,两位互不影响。

class Solution {
public:
    int maximumNumberOfStringPairs(vector<string>& words) {
        unordered_set<int> set;
        int ans = 0;
        for(int i = 0; i < words.size(); i++){
            if(set.count(words[i][1] * 100 + words[i][0]))  ans++;
            set.insert(words[i][0] * 100 + words[i][1]);
        }
        return ans;
    }
};

如果把后边两部反过来会出现一些问题,先插入后判定,那么比如cc、aa、bb这种重叠的字符串就会被多算一次产生错误

            for(int i = 0; i < words.size(); i++){
            set.insert(words[i][0] * 100 + words[i][1]);
            if(set.count(words[i][1] * 100 + words[i][0]))  ans++;
        }
       

【1.18】2171.拿出最少数目的魔法豆 

2171. 拿出最少数目的魔法豆icon-default.png?t=N7T8https://leetcode.cn/problems/removing-minimum-number-of-magic-beans/

这道题目其实暴力做法非常容易,但是不通过,数据集太大了。如果想减少复杂度的话,可以先排序分段进行处理,也就是用排序+前缀和来解决 

方法一:暴力(不通过)

class Solution {
public:
    long long minimumRemoval(vector<int>& beans) {
        vector<long long> res(beans.size(),0);
        for(int i = 0; i < beans.size(); i++){
            for(int j = 0; j < beans.size(); j++){
                if(beans[j] >= beans[i]){
                    res[i] += beans[j] - beans[i];
                }
                else{
                    res[i] += beans[j];
                }
            }
        }
        long long ans = 9999999999;
        for(int i =0; i < beans.size();++i){
             if(res[i] < ans){
                 ans = res[i];
             }
        }
        return ans;
        
    }
};

 暴力两个for直接不通过

方法二:排序 + 前缀和

//排序 + 前缀和
class Solution {
public:
    long long minimumRemoval(vector<int>& beans) {
        sort(beans.begin(),beans.end());
        //前缀和
        vector<long long> sum(beans.size() + 1,0);//让sum【0】 = 0
        for(int i = 0; i < beans.size(); i++){
            sum[i + 1] = sum[i] + beans[i];
        }
        long long res = LLONG_MAX/2;
        int n = beans.size();
        for(int i = 0; i < n; i++){
            res = min(res,sum[i] + sum[n] - sum[i + 1] - (n -1L -i) * beans[i]);
        }
        return res;
    }
};

先排序以后,创建一个数组储存前缀和,这时候要注意新数组的个数是size + 1,然后循环迭代赋值。

在后边的时候,设置res为LLONG_MAX/2,然后看图按照逻辑操作就行,比i这个地方的值小的一律置为0,比它大的置为beans【i】 

【1.20】2788.按分隔符拆分字符串

2788. 按分隔符拆分字符串icon-default.png?t=N7T8https://leetcode.cn/problems/split-strings-by-separator/

该题目是一个比较简单的模拟题,主要是积累getline函数的使用方法 

1.getline(ss, sub, separator) 的作用是从 std::stringstream 对象 ss 中读取一行(以 separator 为分隔符),并将结果存储在字符串 sub 中。istream& getline(istream& is, string& str, char delim);is: 输入流,str: 存储读取的行的字符串。delim: 指定的分隔符,getline 会根据这个分隔符进行拆分。 

class Solution {
public:
    vector<string> splitWordsBySeparator(vector<string>& words, char separator) {
        vector<string> res;
        for(string &word : words){//遍历输入的字符串数组
            stringstream ss(word);//使用字符串流 stringstream 对每个字符串进行处理,后边要用getline
            string str;
            while(getline(ss,str,separator)){
                if(!str.empty()) {res.push_back(str);}
            }
        }
        return res;
    }
};

2.记住&在这里是引用的意思,函数可以直接调用或者修改传递的字符串,而不是对其副本进行操作增加效率。

【1.21】410.分割数组的最大值

410. 分割数组的最大值icon-default.png?t=N7T8https://leetcode.cn/problems/split-array-largest-sum/

本题是一个hard题,分析题目,要求 最大值最小,确定思路不是二分就是dp,本题我们用二分求解。

先分析                                                                     每段和越小——》分出的段越多 ,

假设我分了                                       一个段落和最大值为sum 对应的段落为——》k1>k

代表着 段落分多了,减少分的段,就要每段和大一点。 所以小于等于sum那一部分就可以删掉了(因为要减少段,要每段和大一点)

这里的k个可以理解为至多。

class Solution {
  
public:
    int splitArray(vector<int>& nums, int k) {
        auto maxElement = max_element(nums.begin(), nums.end());
        int l = *maxElement;
        int sum = 0;
        for(int i = 0; i< nums.size(); i++){
              sum += nums[i];
        }
        int r = sum;
        while(l <= r){
            int mid = l + ((r-l) / 2);
            if(check(nums,mid,k)) r = mid - 1;//如果能够分到k段,sum给大了
            else l = mid + 1;
        }
        return l;//当最后l,r,mid指向同一个点的时候,假如满足就是l,假如不满足,l=mid + 1,所以最后返回l
    }

    bool check(vector<int>& nums, int m, int k){
        int cnt = 1;//段数 
        int s = 0;
        for(int i = 0; i < nums.size(); i++){
            if(nums[i] + s <= m) s += nums[i];
            else{
                if(cnt++ == k) return false;
                else s = nums[i]; 
            } 
        }
        return true;
    }
};

这里面有很多值得注意的点,第一个是设置l和r,在设置r的时候就是最少的分段是一整个,所以直接求和,在左边的时候,最多的分段就是每个一段,所以直接找最大值,最大值的时候用一个迭代器,然后不要忘了求迭代器指向的具体值。

在二分法的时候,到了l,r,mid三个指向同一个点时候,考虑,假如这时候满足了,那么这三个点都能表示满足的最大值,如果不满足,那么l要mid+1,最后满足的还是l,所以要返回l。

在check函数中,注意先循环一手,如果小于这个值,就加进去s,如果不满足,先判断段落是不是够k了,如果够了直接false,否则新加一个cnt 

【1.22】670.最大交换

670. 最大交换icon-default.png?t=N7T8https://leetcode.cn/problems/maximum-swap/

该题目主要考察灵活运用to_string 和stoi和swap函数,进行一次遍历。看题解以后,还有一种贪心的算法可以借鉴

方法一:遍历 

class Solution {
public:
    int maximumSwap(int num) {
       string stringNum = to_string(num);//to_string转成字符串
       int maxnum = num;
       int n = stringNum.size();
       for(int i = 0; i < n; i++){
           for(int j = i + 1; j < n; j++){
               swap(stringNum[i],stringNum[j]);
               maxnum = max(maxnum , stoi(stringNum));//stoi字符串转成int
               swap(stringNum[i],stringNum[j]);
           }
       } 
       return maxnum;
    }
};

先把给的num转化成string形式,然后对字符串每一个字符进行两两交换,取最大值,在每一次循环中,交换过了要记得交换回来。 

方法二:贪心

只要让靠近右边的最大的数的索引和左边的比这个数小的数的索引交换位置就能保证换完的数是一个最大。先遍历一遍,找到最大的数,然后从右到左看数字是否比这个最大的数大。

但是注意保证在求maxId时候,尽量保证其靠近右侧。

class Solution {
public:
    int maximumSwap(int num) {
    string stringnum = to_string(num);
    int n = stringnum.size();
    int maxId = n - 1;
    int idx1 = -1;
    int idx2 = -1;
    for(int i = n - 1; i >= 0; i--){
       if(stringnum[i] > stringnum[maxId]){
          maxId = i;       
       } 
       else if (stringnum[i] < stringnum[maxId]){
           idx1 = i;
           idx2 = maxId;
       }
    }
    if(idx1 >= 0){
        swap(stringnum[idx1],stringnum[idx2]);
        return stoi(stringnum);
    }
    else return num;
    }
};

【1.23】2765.最长交替子数组

2765. 最长交替子数组icon-default.png?t=N7T8https://leetcode.cn/problems/longest-alternating-subarray/

这道题目第一思路就是双重循环,类似于双指针,让后边的指针指向的数减去前面的数根据奇数偶数分为0和1即可。看题解以后,发现其实可以降低复杂度为o(n)

方法一: 双重循环 

class Solution {
public:
    int alternatingSubarray(vector<int>& nums) {
        int res = -1;
        for(int i = 0; i < nums.size(); i++){
            for(int j = i + 1; j < nums.size();j++){
                int length = j - i + 1;
                if(nums[j] - nums[i] == (length + 1) % 2){
                res = max(res,length);
                } 
                else {
                    break;
                }
            }
        }
        return res;
    }
};

 方法二: 单重循环 

考虑到当内层循环在第i个不满足条件时,不需要从初始+1外层循环继续开始,因为如果从初始 +1开始,到第i个一定会断,而且长度要比原来的要小。

所以可以直接从i+1继续遍历,但是要注意第i个断了以后,要考虑假如第i个减第i-1个正好是1的时候需要给在新的数组中算上。

class Solution {
public:
    int alternatingSubarray(vector<int>& nums) {
        int res = -1;
        int j = 0;//前指针
        for(int i = 1; i < nums.size(); i++){
                int length = i -j +1;
                if(nums[i] - nums[j] == (length + 1) % 2){
                res = max(res,length);
                } 
                else {
                   if(nums[i] - nums[i-1] == 1){
                       j = i-1;
                       res = max(res,2);  
                   }
                   else{
                       j = i;
                   }
                }
            }
        return res;
    }
};

【1.24】2865.美丽塔

【1.24】2866.美丽塔Ⅱ

2865. 美丽塔 Iicon-default.png?t=N7T8https://leetcode.cn/problems/beautiful-towers-i/

本题可以直接暴力求解,外层遍历让每一个值都当做峰值,内层循环从中间向两边都递减。 同时补充了一道美丽塔Ⅱ

方法一:暴力枚举 

class Solution {
public:
    long long maximumSumOfHeights(vector<int>& maxHeights) {
        long long res = 0;
        int n = maxHeights.size();
        for(int i = 0; i < n; i++){//外层分别设为峰顶
              int   pre = maxHeights[i];
              long long sum = pre;
              for(int j = i - 1 ;j >= 0; j--){
                pre = min(pre,maxHeights[j]);
                sum += pre;
              }
               int  suf = maxHeights[i];
              for(int j = i + 1; j < n; j++){
                  suf = min(suf,maxHeights[j]);
                  sum += suf;
              }
              res = max(res,sum);
        }
    return res;
    }
};

 值得注意的点数据类型要匹配,在函数种定义的maxheights 的值都是int类型,在比较min的过程中,要求两个数的定义都是int类型,而函数返回的是long long,所以定义的sum值是longlong类型。

整体思路是设置每一个值为峰值,然后都向左向右递减用最小值。然后对sum求最大值即可。

补充题目:2866.美丽塔22866. 美丽塔 IIicon-default.png?t=N7T8https://leetcode.cn/problems/beautiful-towers-ii/ 题目一模一样,唯一的区别就是数据量大,不让用枚举了。

这时候就用到第二个方法,单调栈。 

方法二:单调栈

由于题目要求一个峰值,然后两边都是单调的,而且两边逻辑一样,所以我们可以考虑一边,每一边都使用单调栈,对单调栈的操作,首先要进行一个while的pop,因为我们要求栈内元素单调递增的,当出现下边这种情况的时候,为了迁就后边的元素,前面的要弹出(但是要注意我们栈里存储的是下标index),弹出以后,如果这个栈是空的,就让i以前的数都等于maxHeights【i】即可,如果有元素,那么把以前的元素加着pre【top -1】,让后边的都等于maxHeights【i】即可。最后别忘了把下标push进去。然后右边是同理的,最后别忘了峰顶算了两次,减去一次。

class Solution {//单调栈解法,时间复杂度o(n)
public:
    long long maximumSumOfHeights(vector<int>& maxHeights) {
        int n = maxHeights.size();
        long long res = 0;
        vector<long long>pre(n), suf(n);
        stack<int> stk1,stk2;//两个栈存放当前峰值左/右构造的最大值
    for(int i = 0; i < n; ++i){
        while(!stk1.empty() && maxHeights[stk1.top()] > maxHeights[i]){ stk1.pop();}
        if(stk1.empty()){pre[i] = (long long )(i + 1) * maxHeights[i];}
        else{
            pre[i] = pre[stk1.top()] + (long long )(i - stk1.top()) * maxHeights[i];
        }
        stk1.push(i);//把下标push进去
    }
    for(int i = n -1; i >= 0; --i){
        while(!stk2.empty() && maxHeights[stk2.top()] > maxHeights[i]){stk2.pop();}
        if(stk2.empty()) {suf[i] = (long long )(n - i) * maxHeights[i];}
        else{
            suf[i] = suf[stk2.top()] + ((long long)(stk2.top() - i)) * maxHeights[i];
        }
        stk2.push(i);
         res = max(res,suf[i] + pre[i] - maxHeights[i]);//maxHeights[i]算了两遍
    }
    return res;
    }
};

【1.25】2859.计算k置下标对应元素的和

2859. 计算 K 置位下标对应元素的和icon-default.png?t=N7T8https://leetcode.cn/problems/sum-of-values-at-indices-with-k-set-bits/

 本题其实就是一个求二进制的简单题目,大概率会想到用位运算解决

在c++中&表示的是按位与运算。当计算二进制中有多少个1的时候,可以配合位元算

num & (1 << i);//统计每一位上有1的数
sum++;//配合sum++可以统计二进制中是1的位的个数

 注意一点这个循环的大小,因为是int类型,最高就是32位,直接小于32就行

class Solution {
public:
    int sumIndicesWithKSetBits(vector<int>& nums, int k) {
        int res = 0;
       for(int i = 0; i< nums.size();i++){
           if(count(i) == k){
                res+= nums[i];
           }
       }
       return res;
    }
    int count(int num){
        int sum = 0;
        for(int i = 0; i < 32; i++){
            if(num & (1 << i)){
                sum++;
            }
        } 
         return sum;
    }
};

 除了位元算,还有一种可以求一个数二进制的置为1的个数方法

int count(int num){
        int sum = 0;
        while(num){
            sum += (num % 2);
            num /= 2;
        }
         return sum;
    }

【1.26】2846.边权重均等查询

2846. 边权重均等查询icon-default.png?t=N7T8https://leetcode.cn/problems/minimum-edge-weight-equilibrium-queries-in-a-tree/

2500+分hard题,超出水平范围太多,暂时跳过。 

【1.27】2861.最大合金数

2861. 最大合金数icon-default.png?t=N7T8https://leetcode.cn/problems/maximum-number-of-alloys/

看题目“所有合金都需要由同一台机器制造。”又给了个最大值budget,让求取得的最大合金数,也满足二段性,那就是二分法求解了,因为设置这个mid,当我们最大数设置为mid,那么比mid小的数就都不能取了。

 1.设置这个二分,第一个点就是最大值可以稍微的小一点,我们可以设置为budget/1(单价最小就是1)+ 库存的最大量(其实根据木桶效应,应该是库存的最小量)

2.先置为false,遍历每一个机器的花费,再内层遍历每一个零件,求出每个零件加起来总共的spend,再和budget比对,如果比这个小,那么这个mid可以取,就进行变换,置为true,如果不行,再改right

3.在内层遍历的时候,要考虑if(composition[i][j] * (long)mid - stock[j] > 0),这个很关键,库存够了就置为0,不能是负数,我一直卡在这。

//花费小于budget,求能创造合金的最大值,二分
class Solution {
public:
    int maxNumberOfAlloys(int n, int k, int budget, vector<vector<int>>& composition, vector<int>& stock, vector<int>& cost) {
        int left = 0;
//右侧值不用设置太大,考虑最大值  
           auto max = max_element(stock.begin(),stock.end());   
        int right = *max + budget;
        int ans = 0;
        while(left <= right){
            int mid = left + ((right -left) / 2);//设置的最大值,当这个满足的时候,比他小的都不合适
            bool flag = false;//如果可以制造出就用true
            //遍历一遍每台机器花费
            for(int i = 0; i < k; i++){
                long spend = 0;
                for(int j = 0; j < n; j++){
                    if(composition[i][j] * (long)mid - stock[j] > 0){
                    spend += (composition[i][j] * (long)mid - stock[j]) * cost[j];}
                }
                if(budget >= spend){
                    flag = true;
                    break;//第i类就能搞定了
                }
            }
            if(flag){
                  ans = mid;
                  left = mid + 1;
            }
            else{
                right = mid -1;
            }
     }
     return ans;
    }
};

【1.28】365.水壶问题

365. 水壶问题icon-default.png?t=N7T8https://leetcode.cn/problems/water-and-jug-problem/

这个问题除了用深度优先搜索(也是刚会用的),还可以用数学方法更好操作,贝祖定理解决,但是需要考虑的问题比较多。

1.先分析一下,这些操作的实质其实这个水的总量变化只能是x或者是y,因为首先操作的结果至少有一个桶是空的有一个是满的(把一个倒掉,或者加满),假如说把一个半满的水加满或者倒掉是没有意义的,因为等价于一开始就把它置为0或者加满。

2.所以我们的目标变成了找到一堆数组a和b来满足ax + by = z,那么只需满足z <= x + y的情况下, 如果a >  0,b > 0一定ok,如果a  < 0,操作就是先给b加满,把b到给a,a倒掉,再把b剩下的到给a,如此反复。如果是b < 0 ,把操作反过来就行。

3.贝祖定理表示,如果要实现ax + by = z,只需要z整除x和y的最大公约数即可。

class Solution {
public:
    bool canMeasureWater(int jug1Capacity, int jug2Capacity, int targetCapacity) {
        if(jug1Capacity + jug2Capacity < targetCapacity){
            return false;
        }
        //这里贝组定理用之前考虑x或者y万一是0没有最大公约数
        if(jug1Capacity == 0 ||jug2Capacity ==0){
            return targetCapacity == 0 || jug1Capacity + jug2Capacity == targetCapacity; 
        }
        else{
            return targetCapacity % gcd(jug1Capacity,jug2Capacity) == 0;
        }

    }
};

【1.29】514.自由之路

514. 自由之路icon-default.png?t=N7T8https://leetcode.cn/problems/freedom-trail/

这是一道困难题,思路能想到是用dp来解,但是解法还是有一定难度的。分为几个步骤,逻辑要清晰。

  1. 代码主要分为几个步骤,首先是对king进行字母到位置的映射,然后创建dp初始化,然后dp函数的实现。
  2. 创建位置索引的时候,这种映射的构建是为了在后续动态规划的过程中更方便地获取每个字符在环形拨盘上的位置信息。
  3. dp初始化初始值给个最大值

  4. 动态规划转移要分类,在i==0的时候第一个字符,不需要考虑前一个字符的位置,所以分开,对于不是第一个字符要进行一遍循环(对上一个字符的位置循环),进行dp函数的编写,最后输出dp最后一行的最小值

  5. 对于这种环形的可以顺时针逆时针转动的函数求最小值,这个函数值得记一下,加一个len就能完美解决负数问题。

    class Solution {
    public:
    
        int getMin(int len,int a, int b){
            return min((len + a - b) % len ,(len + b - a) % len);
        }
        int findRotateSteps(string ring, string key) {
    //第一步先建立ring中字母的位置索引
     vector<int> pos[26];
     for(int i = 0; i < ring.size(); i++){
         pos[ring[i] - 'a'].push_back(i);
     }
     //第二步,创建dp初始化
     vector<vector<int>> dp(key.size(),vector<int>(ring.size(),INT_MAX));
    
     //第三步 动态规划状态转移
     for(int i = 0; i < key.size(); i++){//遍历key的每个字符
        for(auto j : pos[key[i] - 'a']){//遍历key【i】在圆盘出现的每个位置
         if(i == 0){
             dp[i][j] = min(dp[i][j],0 + getMin(ring.size(),0,j) + 1);
             continue;
         }
         //如果不是第一个,遍历前一个的字符的位置状态
         for(auto k : pos[key[i - 1] - 'a']){
             dp[i][j] = min(dp[i][j],dp[i - 1][k] + getMin(ring.size(),k,j) + 1);
         } 
            }
        }
        return *min_element(dp.back().begin(),dp.back().end());
    
        }
    };

 【1.30】2808.使循环数组所有元素相等的最少秒数

2808. 使循环数组所有元素相等的最少秒数icon-default.png?t=N7T8https://leetcode.cn/problems/minimum-seconds-to-equalize-a-circular-array/

1900分左右的题目,用哈希表解决,思路不算难,主要是昨天的题给了思路,一看题目就知道是一个圆环形状的,左右都能走。哈希表存一下出现的元素的索引i用数组存,因为可能有多个。按照圆环找mx/2的最小值就行。 

  1.  先搞个哈希表,每个nums【i】对应的i值存到数组里,为了后续遍历的时候用
  2. 按照圆的方案去找相邻最远的值,求每个nums【i】的数值里距离最远的值,然后找最小值求出来即可。
  3. 在找个遍历中,先+n为了找圆环情况两个的距离,后边遍历的是相邻的。
class Solution {
public:
    int minimumSeconds(vector<int>& nums) {
        unordered_map<int,vector<int>> map;
        int res = nums.size();
        //先搞个哈希表,每个nums【i】对应的i值存到数组里
        for(int i = 0; i < nums.size(); i++){
            map[nums[i]].push_back(i);
        }
        //不要忘了找圈
        for(auto&  s : map){
            int mx  = s.second[0] + nums.size() - s.second.back();
            for(int i = 1; i < s.second.size(); i++){
                mx = max(mx, s.second[i] - s.second[i - 1]);
            }
         res = min(res, mx/2);
        }
        return res;
    }
};

【1.31】2760.找出不同元素数目差数组

2670. 找出不同元素数目差数组icon-default.png?t=N7T8https://leetcode.cn/problems/find-the-distinct-difference-array/

哈希表,难点在于建立数组的时候的范围还有,运用哈希表去统计“从第i个数到最后一个数不同的数的数量”和“从第一个数到第i个数不同数的数量”

class Solution {
public:
    vector<int> distinctDifferenceArray(vector<int>& nums) {
        int n = nums.size();
        vector<int> res;
        unordered_set<int> st;
        vector<int> suf(n + 1);//为了满足后边运算的时候suf(n -1)
        for(int i =  n - 1; i > 0; i--){
            st.insert(nums[i]);
            suf[i] = st.size();//从第i开始到结尾不同值的数量
        }
        st.clear();
        for(int i = 0; i < n; i++){
            st.insert(nums[i]);//从第一个数开始到第i个数不同值的数量
           res.push_back(st.size() -suf[i + 1]);
        }
        return res;
    }
};

创建suf数组大小是n+1的时候,是为了后边满足运算的时候suf(n  - 1). 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1424236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务攻防-开发组件安全Solr搜索Shiro身份Log4j日志本地CVE环境复现

知识点&#xff1a; 1、J2EE-组件安全-Solr-全文搜索 2、J2EE-组件安全-Shiro-身份验证 3、J2EE-组件安全-Log4J-日志记录 章节点&#xff1a; 1、目标判断-端口扫描&组合判断&信息来源 2、安全问题-配置不当&CVE漏洞&弱口令爆破 3、复现对象-数据库&中间…

Qt 5.9.4 转 Qt 6.6.1 遇到的问题总结(三)

1.QSet: toList 中的toList 函数已不存在&#xff0c;遇到xx->toList改成直接用&#xff0c;如下&#xff1a; 2.开源QWT 图形库中QwtDial中的 setPenWidth 变成 setPenWidthF函数。 3.QDateTime 中无setTime_t 改为了setSecsSinceEpoch函数。 4.QRegExp 类已不存在 可以用Q…

STM32的中断系统详解

一、什么是中断 中断是指CPU在正常执行程序时&#xff0c;遇到外部/内部的紧急事件需处理&#xff0c;暂停当前程序的执行&#xff0c;转而去处理紧急事件&#xff0c;待事件处理完毕后&#xff0c;返回被打断的程序继续执行&#xff0c;这个过程就称之为中断。 中断的意义&a…

单片机驱动多个ds18b20

目录 1设计内容 2ds18b20介绍 2.1传感器引脚及原理图 2.2寄存器配置 3程序实现 3.1配置初始化 3.2配置寄存器 3.3ROM读取 3.4温度读取 1设计内容 通过51单片机&#xff0c;读取总线上挂载的多个ds18b20的温度信息。 如下图&#xff0c;成功读取到3路温度数据。 2ds18…

python打造光斑处理系统4:裁切光斑感兴趣区域

文章目录 图像裁切给定坐标裁切手动阈值裁切 光斑处理&#xff1a;python处理高斯光束的图像 光斑处理系统&#xff1a;程序框架&#x1f31f;打开图像&#x1f31f;参数对话框/伪彩映射 图像裁切 一般来说&#xff0c;光斑只占图像很小一部分&#xff0c;为了更好的观感和更…

Oracle 面试题 | 04.精选Oracle高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

中国城市统计年鉴,多种数据格式可用,时间覆盖1995-2022年

基本信息. 数据名称: 中国城市统计年鉴 数据格式: pdf、jpg、excel、html不定 数据时间: 1995-2021不定 数据几何类型: 文本 数据坐标系: —— 数据来源&#xff1a;国家统计局 示例数据: 目录一、全国城市行政区划1-1 城市行政区划和区域分布1-2 分地区城市情况一…

隧道穿越:隧道穿透技术介绍

后面会进行一些隧道穿越的实验&#xff0c;因此在本篇中这里先介绍一些有关隧道穿越的技术知识点 隧道和隧道穿透 隧道是一种通过互联网基础设施在网络之间传递数据的方式&#xff0c;设计从数据封装、传输到解包的全过程&#xff0c;使用隧道传递的数据&#xff08;或者负载…

存内计算——发展史与近期成果

存内计算的概念早在上个世纪就已经被提出&#xff0c;但当时的人们寄希望于通过优化处理器设计以及工艺制程的升级&#xff0c;来获得性能和能效比的提升&#xff0c;存内计算的研究仅停留在理论阶段。随着大数据时代的到来&#xff0c;存内计算由于其结构特点以及摩尔定律的“…

WiFi基础知识介绍(超详细)

1.WiFi专业名词概念 AP(Access Point):无线接入点&#xff1a;这个概念特别广&#xff0c;在这里&#xff0c;用大白话说&#xff0c;你可以把CC3200当做一个无线路由器&#xff0c;这个路由器的特点不能插入网线&#xff0c;没有接入Internet&#xff0c;只能等待其他设备的链…

springCloud gateway 防止XSS漏洞

springCloud gateway 防止XSS漏洞 一.XSS(跨站脚本)漏洞详解1.XSS的原理和分类2.XSS漏洞的危害3.XSS的防御 二.Java开发中防范XSS跨站脚本攻击的思路三.相关代码&#xff08;适用于spring cloud gateway&#xff09;1.CacheBodyGlobalFilter.java2.XssRequestGlobalFilter.java…

CAS和自旋的区别

一、前言 我们在面试的时候&#xff0c;经常性的会遇到一些关于锁的问题&#xff0c;尤其是面试官会提出问题&#xff1a;你对锁的了解多么&#xff1f;你知道锁的原理么&#xff1f;等等问题&#xff0c;于是也就会有后续延伸出来的&#xff1a;你知道CAS么&#xff1f;你知道…

Habitat环境学习二:导航任务中的Habitat-sim基础Habitat-sim Basics for Navigation

导航任务在Habitat-sim任务中的实现 官方教程概述重要概念1、Hello World程序1.0.1 基础设置Basic settings1.0.2 模拟器设置Configurations for the simulator1.0.3 创建模拟器实例1.0.4 初始化Agent1.0.5 导航和探索 官方教程 Habitat是一个高效的真实的3D模拟器&#xff0c…

基于C++的面向对象程序设计:类与对象的深入剖析

面向对象程序设计的基本特点 面向对象程序设计的基本特点包括&#xff1a;抽象、封装、继承、多态。 抽象 抽象是指对具体问题或对象进行概括&#xff0c;抽出其公共性质并加以描述的过程。一般情况抽象分为数据抽象和行为抽象&#xff0c;其中数据抽象是指一个对象区别于另…

idea/webstorm 创建Vue实例 Unresolved type Vue 处理方法

1.电脑本地安装node.js 官网下载 2. 其他: 未排除变量,前期试错(以下步骤配置了,但不确定对解决问题是否有帮助)

第 10 章:在C++中使用协程进行系统编程

最后一章专门介绍了一项对系统编程非常有用的功能&#xff0c;但这在C标准中相对较新。协程对象迅速找到了应用&#xff0c;成为了一等公民的状态机对象。它们的强大之处在于隐藏了协程帧后面的逻辑。请注意&#xff0c;这是一个高级主题&#xff0c;且C的协程接口既不简单也不…

216961-98-7,BODIPY 493/503 NHS 活化酯,可以应用于分子生物学等领域中

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;216961-98-7&#xff0c;BODIPY 493/503 NHS 活化酯&#xff0c;BODIPY 493/503 NHS ester&#xff0c;BODIPY 493/503 SE 一、基本信息 产品简介&#xff1a;BODIPY 493/503 NHS ester是一种特殊的染料&#xff0c…

【lesson4】高并发内存池ThreadCache(线程缓存)层实现

文章目录 ThreadCache层的结构申请内存逻辑释放内存逻辑自由链表的实现自由链表的成员变量自由链表的成员函数自由链表的完整实现 ThreadCache申请内存过程的实现ThreadCache需要的成员变量ThreadCache需要的成员函数ThreadCache.h文件代码Allocate的实现Deallocate的实现 封装…

02、全文检索 ------ Solr(企业级的开源的搜索引擎) 的下载、安装、Solr的Web图形界面介绍

目录 Solr 的下载和安装Solr的优势&#xff1a;Lucene与Solr 安装 Solr1、下载解压2、添加环境变量3、启动 Solr Solr 所支持的子命令&#xff1a;Solr 的 Core 和 Collection 介绍Solr 的Web控制台DashBoard&#xff08;仪表盘&#xff09;Logging&#xff08;日志&#xff09…