LeetCode_Dec_1st_Week

Posted KuoGavin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode_Dec_1st_Week相关的知识,希望对你有一定的参考价值。

December 6th : 1816. 截断句子
December 7th : 1034. 边界着色
December 8th : 689. 三个无重叠子数组的最大和
December 9th : 794. 有效的井字游戏
December 10th : 748. 最短补全词
December 11st : 911. 在线选举
December 12nd : 709. 转换成小写字母


December 6th : 1816. 截断句子

今天我就24周岁啦!承蒙厚爱~ ,得到了人生中的第一个乐高模型,许愿今后能开起不差的B级车吧~💪💪💪!感谢亲爱的!刷个题助助兴 ~

读过题,单词间是固定单个空格的,那么就记录还剩余多少单词没有加上,同时维护窗口,窗口的左右字符都是空格,则说明是一个单词。这样遍历整个输入字符串,然后截取出前k个单词,并拼接成字符串返回即可。

class Solution 
public:
    string truncateSentence(string s, int k) 
        string ret;
        for(int i = 0; i < s.size(); ++i) 
            if(s[i] == ' ') continue;
            if(k == 0) break; //单词截取完了
            int l = i, r = i; //窗口左右边界
            while(r < s.size() && s[r] != ' ') r++; //没遇到空格,则扩大窗口
            for(int j = i; j < r; ++j) ret += s[j]; //把窗口中单词加到结果字符串上
            k--; i = r;  //更新剩余单词数,同时更新i的值,以作为新的窗口的初始化值
            if(k != 0) ret += " "; //如果不是末尾单词,要加上空格作为分隔符
        
        return ret;
    
;

December 7th : 1034. 边界着色

将连通集的边界(网格边界,和不同颜色的边界)染成指定的颜色。就相当于小岛的题,使用 d f s dfs dfs或者 b f s bfs bfs均可。这里使用 d f s dfs dfs,其中如何求得边界是关键,当 d f s dfs dfs到网格边界或者发现相邻格子和初始格子的颜色不一致时,则可判定当前所遍历到的格子是边界格子。再将边界的格子着上指定的颜色即可。

class Solution 
public:
    vector<vector<int>> colorBorder(vector<vector<int>>& grid, int row, int col, int color) 
        if(row < 0 || row >= grid.size() || col < 0 || col >= grid[0].size()) return grid;
        visited = vector<vector<int>>(grid.size(), vector<int>(grid[0].size(), 0)); //初始化访问记录
        dfs(grid, visited, row, col, color); //进行dfs
        for(int i = 0; i < grid.size(); ++i) //进行边界着色
            for(int j = 0; j < grid[0].size(); ++j)
                if(visited[i][j] == 2) grid[i][j] = color;
        return grid;
    

private:
    vector<int> direction = -1, 0, 1, 0, -1; //方向向量
    vector<vector<int>> visited; //记录是否访问过,进行dfs剪枝,同时能记录边界
    void dfs(vector<vector<int>>& grid, vector<vector<int>>& visited, int row, int col, int color) 
        if(row < 0 || row >= grid.size() || 
           col < 0 || col >= grid[0].size() || visited[row][col]) return; //如果坐标不合法或已经访问过则返回
        visited[row][col] = 1; //标记已经遍历过该坐标
        for(int i = 0; i < 4; i++)  //遍历当前坐标上下左右的四个坐标
            int row_new = row + direction[i], col_new = col + direction[i+1];
            if(row_new >= 0 && row_new < grid.size() && 
                col_new >= 0 && col_new < grid[0].size() &&
                grid[row][col] == grid[row_new][col_new]) //如果符合,则说明没有到达连通集边界
                dfs(grid, visited, row_new, col_new, color);
            else visited[row][col] = 2; //如果不符合,则说明到达连通集边界,将当前坐标标记为2
        
    
;

December 8th : 689. 三个无重叠子数组的最大和

使用三个大小为k的滑动窗口。设 s u m 1 sum_1 sum1为第一个滑动窗口的元素和,该滑动窗口从 [ 0 , k − 1 ] [0,k-1] [0,k1]开始; s u m 2 sum_2 sum2为第二个滑动窗口的元素和,该滑动窗口从 [ k , 2 k − 1 ] [k,2k-1] [k,2k1]开始; s u m 3 sum_3 sum3为第三个滑动窗口的元素和,该滑动窗口从 [ 2 k , 3 k − 1 ] [2k,3k-1] [2k,3k1]开始。

同时向右滑动三个窗口,维护 m a x S u m 12 maxSum_12 maxSum12(表示第一二窗口的最大和)及对应位置。每次滑动,计算当前的 m a x S u m 12 maxSum_12 maxSum12 s u m 3 sum_3 sum3之和。统计过程中 m a x S u m 12 + s u m 3 maxSum_12+sum_3 maxSum12+sum3的最大值和各窗口的对应位置。

由于我们是从左向右遍历,当且仅当三个窗口元素和大于的最大值和时才修改元素和,所以更够保证是最小字典序的。

class Solution 
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) 
        vector<int> ans;
        int sum1 = 0, maxSum1 = 0, maxSum1Idx = 0;
        int sum2 = 0, maxSum12 = 0, maxSum12Idx1 = 0, maxSum12Idx2 = 0;
        int sum3 = 0, maxTotal = 0;
        for (int i = k * 2; i < nums.size(); ++i)  //将第三个窗口的初始左边界作为i的初始值
            //如果没到第三个窗口的初始右边界,则进行窗口中的值的累加
            sum1 += nums[i - k * 2];
            sum2 += nums[i - k];
            sum3 += nums[i];
            //当i已经超出第三个窗口的初始右边界时,所有窗口中的值都会发生变化
            if (i >= k * 3 - 1) 
                if (sum1 > maxSum1)  //第一个窗口找到了较大和
                    maxSum1 = sum1; //更新第一个窗口的最大值
                    maxSum1Idx = i - k * 3 + 1; //更新记录的下标
                
                //第二个窗口和和第一个窗口最大和相加大于先前计算的第一二窗口的最大和
                if (maxSum1 + sum2 > maxSum12)  
                    maxSum12 = maxSum1 + sum2; //更新第一二窗口的最大和值
                    maxSum12Idx1 = maxSum1Idx; //更新此时的第一个窗口的左边界
                    maxSum12Idx2 = i - k * 2 + 1; //更新此时的第二个窗口的左边界
                
                //第一二窗口的最大和和第三个窗口和相加大于之前三个窗口的最大和
                if (maxSum12 + sum3 > maxTotal)  
                    maxTotal = maxSum12 + sum3; //更新三个窗口最大和
                    ans = maxSum12Idx1, maxSum12Idx2, i - k + 1; //更新左边界坐标
                
                //去除旧左边界的值,为接下来的for轮窗口右移做准备
                sum1 -= nums[i - k * 3 + 1]; 
                sum2 -= nums[i - k * 2 + 1];
                sum3 -= nums[i - k + 1];
            
        
        return ans;
    
;

December 9th : 794. 有效的井字游戏

搞清楚怎么才是有效的是解题的关键:

  • 首先是X起手,所以X的个数要比O多一个或者是一样多;
  • ① 其次,如果X比O多,则不存在O结束游戏的情况(一行/列,或主/副对角线全是O),这样X的个数才可能比O多;
  • ② 同理,如果X和O一样多,则说明不存在X结束游戏的情况(一行/列,或主/副对角线全是X)。

理清了只有①②这两种情况才是有效,再将思路编写出代码即可。

class Solution 
public:
    bool validTicTacToe(vector<string>& board) 
        int cnt = 0, w[128]; //cnt记录XO的差值,O为负
        //得到差值,只有两种情况,一是X比O多一个此时cnt==1,二是X和O一样多此时cnt==2
        for(auto&& s: board) for(char c: s) cnt += c == 'O' ? -1 : c == 'X'; 
        //扫描各行各列,查找是否有一行或一列都是X或O的情况,
        //如果不全是,则不会映射到X或O,即w['X']或w['O']为0
        for(int i = 0; i < 3; ++i)
            ++w[board[i][0] | board[i][1] | board[i][2]];   
            ++w[board[0][i] | board[1][i] | board[2][i]];    
        
        //扫描主对角线和副对角线
        ++w[board[0][0] | board[1][1] | board[2][2]];
        ++w[board[0][2] | board[1][1] | board[2][0]]; 
        //只有两种情况下,才是可能的:
        //一是X比O多(cnt==1),且O不存在于一行/列,或主/副对角线(w['O']==0)
        //二是X和O一样多(cnt==0),且X不存在于一行/列,或主/副对角线(w['X']==0)
        return cnt == 1 && !w['O'] || cnt == 0 && !w['X'];
    
;

December 10th : 748. 最短补全词

把题意弄清楚,然后就是简单的字符串遍历和频次统计的问题了。

class Solution 
public:
    string shortestCompletingWord(string licensePlate, vector<string>& words) 
        int destIdx = -1; bool flg = true;
        int plate[26] = 0, word[26] = 0;
        for(int i = 0; i < licensePlate.size(); ++i) 
            if(isLetter(licensePlate[i])) plate[getIdx(licensePlate[i])]++;
        for(int i = 0; i < words.size(); ++i) 
            memset(word, 0, 26*sizeof(int));
            for(int j = 0; j < words[i].size(); ++j) 
                if(isLetter(words[i][j])) word[getIdx(words[i][j])]++;
            for(int k = 0; k < 26; ++k) 
                if(plate[k] > word[k]) flg = false; break;
            if(flg) 
                if(destIdx == -1) destIdx = i;
                else destIdx = words[destIdx].size() > words[i].size() ? i : destIdx; 
            
            flg = true;
        
        return words[destIdx];
    
private:
    bool isLetter(char ch) 
        if(ch <= 'z' && ch >= 'a' || ch <= 'Z' && ch >= 'A') return true;
        else return false;
    
    int getIdx(char ch) 
        if(ch 以上是关于LeetCode_Dec_1st_Week的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode_Nov_1st_Week

LeetCode_Dec_2nd_Week

LeetCode_Dec_2nd_Week

LeetCode_Dec_3rd_Week

LeetCode_Dec_3rd_Week

LeetCode_Dec_3rd_Week