带你学算法:LeetCode 第 201 场周赛 题解

LeetCode 题解哪家强?来GTA逛一逛!
我们的公众号:Grand Theft Algorithm
每天一道LeetCode题解
每周七种不一样的题型
每月六个周赛大礼包
只要你来,我们就包你学会!

题目1:1544. 整理字符串

思路:栈

签到题,多种解法。

维护一个栈,遍历字符串,每遍历到一个字符就将其与栈顶元素比较。

实现细节:

比较前要保证栈不为空。

最后从栈中提取时,要注意用 res = a.top() + res,而非 res += a.top(),因为是按照顺序入栈的,所以出栈顺序刚好相反。利用 res = a.top() + res 将每次出栈的元素放到字符串最前面,就是正确的顺序了。

代码:

class Solution {
  public:
     string makeGood(string s) {
       stack<char> a;
       int len = s.size();
       a.push(s[0]);
       for(int i = 1; i < len; i++) {
         if(!a.empty() && abs(s[i] - a.top()) == 'a' - 'A') {
           a.pop();
         } else {
           a.push(s[i]);
         }
       }
       string res = "";
       while(!a.empty()) {
         res = a.top() + res;  //逆序输出
         a.pop();
        }
       return res;
    }
};

复杂度分析:

遍历字符串时间复杂度O(N),拼接结果字符串也是O(N),故时间复杂度为O(N);

维护了一个栈,空间复杂度为O(N)。

题目2:1545. 找出第 N 个二进制字符串中的第 K 位

思路:记录翻转次数

首先,设S(n)对应的位数len,则有:


image.png

且我们可以发现,第k个元素与第len + 1 - k个元素刚好是互为翻转,也即一个是0,一个是1,这是因为前一半与后一半互为反转的翻转。

既然这样,我们不需要算出给定 n 对应的 S(n) 究竟是什么样的字符串,而只需要找到当前这第 k 位的元素是怎么出来的就行了。

也就是说,如果这第 k 位元素在当前字符串S(n)的右半部分,则说明它是通过S(n - 1)拓展而来的,而根据我们上面的分析,其对应的 S(n - 1)中的位置是 len + 1 - k;如果在当前字符串的左半部分,说明其并未翻转,直接从S(n - 1)继承而来。

以此类推,我们就找到了一种方式进行递归,每次将S(n)简化到S(n - 1),最终能够到达 S(1),而在这个过程中,我们每次只需要记录要找的这个元素被翻转的次数即可。

实现细节:

外层while循环保证n自减,内层判断 k 与 len / 2的关系。

若 k == len / 2,根据题意知,该位置是某一次扩展时直接赋值的1,但是经过我们的分析,当前的k有可能是通过某一轮的 k = len + 1 - k得来的,所以在返回值得部分要加上翻转次数 cnt。

若 k > len / 2,则根据上面的分析,我们将翻转次数 cnt++,并将k挪到字符串的左半部分,也即 k = len + 1 - k,以便于下一轮循环的缩小范围。

代码:

class Solution {
  public:
     char findKthBit(int n, int k) {
       int cnt = 0;
       while(n) {
         int len = (1 << n) - 1;
         if(len == 1) {
           return (char)(cnt % 2 + '0');
         }
         if(k > (len + 1) / 2) {
           k = len + 1 - k;
           cnt++;
         } else if(k == (len + 1) / 2) {
           return (char)((cnt + 1) % 2 + '0');
         }
         n--;
       }
     return (char)(cnt % 2 + '0');
   }
};

复杂度分析:

外层循环嵌套复杂度O(N),内层处理复杂度O(1),故总时间复杂度为O(N);

只用了简单变量,空间复杂度为O(1)。

题目3:1546. 和为目标值的最大数目不重叠非空子数组数目

思路:前缀和+哈希优化

最简单的方式是前缀和遍历,然后双重循环嵌套判断两点(i, j)之间的前缀和之差是否为target,时间复杂度为O(N^2),但是本题给定的数据会超时,所以要想办法优化。

我们假设前缀和数组为A,由于前缀和的原理就是判断 A[j] - A[i] == target 是否成立,所以我们可以换一种方式,即判断 A[j] = A[i] + target是否成立,由于题目只要求计算这样的(i, j)对的数量,所以我们利用哈希表直接记录 A[i] + target 的值即可。

从前往后遍历的过程中,我们维护 pos 为当前前缀和,判断当前pos在哈希表中是否出现过,并将 pos + target 记录入哈希表。这样只需要一轮遍历就足够了。

实现细节:

维护unordered_map哈希表,记录pos + target值及其对应出现的位置。

维护res记录满足条件的区间数,也即(i, j)对的数量,维护pos记录当前前缀和。

每次找到满足条件的区间后,res++,由于要求区间不重叠,所以直接清空map。

最为关键的是初始化及每次清空后,要单独赋值mp[target] = 1,这样做的原因是在找到一组区间后,清空map,如果之后跟着的连续几个元素之和刚好为target,要保证能够记录该区间。

代码:

class Solution {
  public:
     int maxNonOverlapping(vector<int>& nums, int target) {
       unordered_map<int, int> mp;
       mp[target] = 1;
       int pos = 0;
       int res = 0;
       for(int x : nums) {
         pos += x;
         if(mp[pos] > 0) {
           pos = 0;
           mp.erase(mp.begin(), mp.end());
           mp[target] = 1;
           res++;
         } else {
           mp[pos + target]++;
         }
       }
       return res;
     }
};

复杂度分析:

数组遍历,时间复杂度为O(N);

利用了哈希表,空间复杂度为O(N)。

题目4:1547. 切棍子的最小成本

思路:动态规划

设dp(i, j)表示以(i, j)为左右端点时,切割木棍的最小成本,显然(i, j)的取值范围只能是0,n以及cuts数组中的某一个数,所以我们将 cuts 数组扩展,加上最左边界0及最右边界n,表示可供切割的所有端点集合。

对于给定的左右端点(i, j),切割木棍的最小成本可以通过遍历该区间内所有可供切割的端点,并更新最小值获得。这样通过递归调用的方式可以将整个问题转换为若干子问题求解。

实现细节:

边界条件:dp(i , i + 1) = 0,这是因为间距为1时无法再进行切割

初始化:dp(i, j) = INT_MAX,便于求解最小值

结合上面的分析,我们遍历(i, j)之间所有可供切割的节点k,并将目前的问题转化为在(i, k)与(k, j)上分别求解最小花费的子问题,且对于节点 k 的切割所需花费为 cuts[j] - cuts[i],所以可以列出动态转移方程如下:


image.png

为便于扩展cuts数组,用新数组a来替代。

为了减少重复计算,利用二维数组记录所有已经求出的dp(i)(j)。

代码:

int a[105];
int dp[105][105];
​
int solve(int l, int r) {
   if (dp[l][r] != INT_MAX) return dp[l][r];
   if (r - l == 1) {
     dp[l][r] = 0;
     return dp[l][r];
   }
   for(int i = l + 1; i <= r - 1; i++) {
     dp[l][r] = min(dp[l][r], solve(l, i) + solve(i, r) + a[r] - a[l]); 
   }
   return dp[l][r];
}
​
class Solution {
  public:
     int minCost(int n, vector<int>& cuts) {
       sort(cuts.begin(), cuts.end());
       int len = cuts.size();
       for(int i = 1; i <= len; i++) {
         a[i] = cuts[i - 1];
       }
       a[0] = 0;
       a[len + 1] = n;

       for(int i = 0; i <= len + 1; i++) {
         for(int j = 0; j <= len + 1; j++) {
           dp[i][j] = INT_MAX;
         }
       }
       int tmp = solve(0, len + 1);
       return tmp;
     }
};</pre>

复杂度分析:

利用记忆化动态规划,每个(i, j)对对应的最小花费最多只会被计算一次,总时间复杂度为O(len^2);

空间复杂度为O(len^2)。

公众号

Leetcode题目不知道从何下手?

面试笔试的算法题老是没有思路?

各类比赛打完了很多题还是不会做?

欢迎来我们的公众号:Grand Theft Algorithm

每天一道Leetcode题解,一起交流学习!

我们的题解包含《程序员面试金典》系列、回溯算法、动态规划、图论、树、链表等,一天一个tag!

周赛、双周赛、牛客专题赛、Codeforce等级赛,只要你想看,我们就给你讲明白!

只有你想不到,没有我们写不到!

欢迎大家关注、点赞!一起进步,一起学习,就在我们的公众号:Grand Theft Algorithm

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342