Leetcode刷题记录 2023/11/20——2023/11/26

11/20 53.最大子数组和

给定一个整数数组nums,找出一个具有最大和的连续子数组(最少包含一个元素),返回其最大和。

题目链接
一题双解,动态规划/分治。
动态规划:dp[i]是以第i个元素为结尾的连续子数组最大和,递推方程为
d p [ i ] = m a x ( d p [ i − 1 ] + n u m s [ i ] , n u m s [ i ] ) dp[i] = max(dp[i-1]+nums[i], nums[i]) dp[i]=max(dp[i1]+nums[i],nums[i])
分治:考虑数组中心点,最大子数组和的出现有三种可能(全在中心点左边/全在中心点右边/跨中心点),跨中心点的最大和由子函数处理,再分治做左右的子数组和作比较。

class Solution {
public:
    int maxSubArray_DC(vector<int>& nums) {
        int n=nums.size();
        return Divide(0, n-1, nums);
    }
    int Divide(int l, int r, vector<int>& nums){
        // cout << l << " " << r << '\n';
        if(l > r) return -2147483647;
        if(l == r) return nums[l];
        int mid = (l+r) >> 1;
        int lsum = Divide(l, mid-1, nums), rsum = Divide(mid+1, r, nums);
        int lrsum = Conquer(l, r, mid, nums);
        cout<< lsum << " " << rsum << " " << lrsum <<'\n';
        return max(lsum, max(rsum, lrsum));
    }
    int Conquer(int l, int r, int mid, vector<int>& nums){
        int lsum = 0, rsum = 0, sum = 0;
        for(int i=mid-1;i>=l;i--){
            sum += nums[i];
            lsum = max(sum, lsum);
        }
        sum = 0;
        for(int i=mid+1;i<=r;i++){
            sum += nums[i];
            rsum = max(sum, rsum);
        }
        return lsum+nums[mid]+rsum;
    }
    int maxSubArray(vector<int>& nums) {
        int n=nums.size(), ans=nums[0];
        vector<int> dp(n, 0);
        dp[0] = nums[0];
        for(int i=1;i<n;i++){
            dp[i] = max(dp[i-1] + nums[i], nums[i]);
            ans = max(dp[i], ans);
        }
        return ans;
    }
};

11/21 2216. 美化数组的最少删除数

给定一个数组nums,从其中删除尽量少的元素,使得数组长度为偶数且所有偶数下标的元素不等于相邻右侧奇数下标。

题目链接
由于删除元素会使其他元素左移填空,不妨假设目前讨论的元素左边已经全部符合美丽数组的要求;
若当前元素是偶数下标且与下一个元素相同,则删除当前元素,并记录删除元素数(删除元素影响后续元素的下标);
如此模拟,若最后数组是奇数长度则额外删去一个元素。

class Solution {
public:
    int minDeletion(vector<int>& nums) {
        int n = nums.size(), ans = 0;
        for(int i=0;i<n;i++){
            if((i-ans)%2 == 0 && i+1 < n && nums[i] == nums[i+1]) ans++;
        }
        if((n - ans)%2) ans++;
        return ans;
    }
};

11/22 2304. 网格中的最小路径代价

给定:
一个下标为0的整数矩阵grid,大小为 m × n m\times n m×ngrid[i][j]代表第i行第j列单元格内的整数值。
一个下标同样为0的整数矩阵movecost,大小为 ( m × n ) × n (m\times n)\times n (m×n)×nmovecost[i][j]是从值i的单元格移动到下一行第j列的单元格的代价。
求从第一行任意单元格出发到达最后一行任意单元格的最小路径代价 = 经过的单元格值之和 + 移动代价之和。

题目链接
保存一个dp[i],代表从值为i的格子一路到达最底层的最低代价。
从最底层值开始自下而上更新dp[i],更新方程为:
d p [ g r i d [ i ] [ j ] ] = m i n ( d p [ g r i d [ i + 1 ] [ k ] ] + m o v e c o s t [ g r i d [ i ] [ j ] ] [ k ] + g r i d [ i ] [ j ] ) dp[grid[i][j]] = min(dp[grid[i+1][k]] +movecost[grid[i][j]][k]+grid[i][j]) dp[grid[i][j]]=min(dp[grid[i+1][k]]+movecost[grid[i][j]][k]+grid[i][j])

class Solution {
public:
    const int INF = 0x3fffffff;
    int minPathCost(vector<vector<int>>& grid, vector<vector<int>>& moveCost) {
        //保存一个dp[i],代表从值为i的格子一路到达最底层的最低代价
        //利用grid自下而上更新,最底层值为0
        int n = grid.size(), m = grid[0].size();
        vector<int> dp(n*m, INF);
        for(int i=0;i<m;i++) dp[grid[n-1][i]] = grid[n-1][i];//初始化最下层
        for(int i=n-2;i>=0;i--){
            for(int j=0;j<m;j++){
                for(int k=0;k<m;k++) dp[grid[i][j]] = min(dp[grid[i][j]], dp[grid[i+1][k]] + moveCost[grid[i][j]][k] + grid[i][j]);
            }
        }
        int ans = INF;
        for(int i=0;i<m;i++) ans = min(ans, dp[grid[0][i]]);
        return ans;
    }
};

11/23 1410. HTML 实体解析器

将HTML代码中的字符实体替换为对应的特殊字符。

题目链接
C++中可以使用哈希的方式进行字符的替换,也可以用正则做替换。
不过需要注意,存在与符号的嵌套情况&amp;amp;,若不断嵌套式替换则有可能导致上述例子被替换为一个&

class Solution {
public:
    string entityParser(string text) {
        unordered_map<string, char> entities = {
            {"&quot;", '"'},
            {"&apos;", '\''},
            {"&amp;", '&'},
            {"&gt;", '>'},
            {"&lt;", '<'},
            {"&frasl;", '/'}
        };
        int len = text.length(), i = 0;
        string ans;
        while(i < len){
            if(text[i] == '&'){
                int j = i + 1;
                while(j < len && text[j] != ';' && j-i <6) j++;
                string sub = text.substr(i, min(j+1, len)-i);
                if(entities.find(sub) != entities.end()){
                    ans += entities[sub];
                    i = j + 1;
                    continue;
                }
            }
            ans += text[i++];
        }
        return ans;
    }
};
class Solution {
public:
    string entityParser(string text) {
        string ans;
        string oldStr = "&quot;";
        string newStr = "\"";
        regex re(oldStr);
        ans = regex_replace(text, re, newStr);
        oldStr = "&apos;"; newStr = '\'';
        regex re1(oldStr);
        ans = regex_replace(ans, re1, newStr);
        oldStr = "&gt;"; newStr = '>';
        regex re3(oldStr);
        ans = regex_replace(ans, re3, newStr);
        oldStr = "&lt;"; newStr = '<';
        regex re4(oldStr);
        ans = regex_replace(ans, re4, newStr);
        oldStr = "&frasl;"; newStr = '/';
        regex re5(oldStr);
        ans = regex_replace(ans, re5, newStr);
        oldStr = "&amp;"; newStr = '&';
        regex re2(oldStr);
        ans = regex_replace(ans, re2, newStr);
        return ans;
    }
};

11/24 2824. 统计和小于目标的下标对数目

给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 target ,请你返回满足 0 <= i < j < n 且 nums[i] + nums[j] < target 的下标对 (i, j) 的数目。

题目链接
暴力可行,排序后做双指针同样可行(但范围50就别折腾自己了)。

class Solution {
public:
    int countPairs(vector<int>& nums, int target) {
        int n = nums.size(), ans = 0;
        for(int i=0;i<n;++i)
            for(int j=i+1;j<n;++j) if(nums[i] + nums[j] < target) ans++;
        return ans;
    }
};

11/25 1457. 二叉树中的伪回文路径

给你一棵二叉树,每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的,当它满足:路径经过的所有节点值的排列中,存在一个回文序列。
请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。

题目链接
若一个序列能够是回文的,则节点值中最多有一个值出现了奇数次,否则一定无法构造回文。
我们可以保存一个数组r,记录节点值出现的次数,随DFS更新,每次到达叶子结点则统计出现次数,确认能否构造回文序列。

class Solution {
public:
    int pseudoPalindromicPaths (TreeNode* root) {
        vector<int> r(10, 0);
        return DFS(root, r);
    }
    int DFS(TreeNode* root, vector<int>& route){
        if(root == NULL) return 0;
        if(root->left == NULL && root->right == NULL){
            route[root->val] ++;
            int center = 0;
            for(int i=0;i<10;i++)
                if(route[i] % 2) center += 1;
            route[root->val] --;
            return center <= 1;
        }
        route[root->val] ++;
        int ans = DFS(root->left, route) + DFS(root->right, route);
        route[root->val] --;
        return ans;
    }
};

11/26 828. 统计子串中的唯一字符

给定一个字符串s,需要返回countUniqueChars(t)的总和,其中t是s的所有子字符串。
countUniqueChars(s)输入一个字符串,返回字符串中所有唯一字符的个数。

题目链接
题目的数据范围 1 0 5 10^5 105决定了不能使用暴力,我们需要从别的方向考虑唯一字符的统计。
由于s中只包含大写英文字符,我们不妨从26个字母本身做考虑:
同一个字母的两次出现之间,所有的子字符串中该字母对唯一字符的贡献我们都归到左字符上;而在一个长为a的字符串内统计最左边元素的出现次数,值即为长度。
因此,我们将唯一字符的统计转化为同一字母出现间距的统计。

class Solution {
public:
    int uniqueLetterString(string s) {
        int ans=0, n=s.length();
        for(int i=0;i<26;i++){
            int l=-1,r=-1;
            for(int j=0;j<n;j++){
                if(s[j]=='A'+i) l=r,r=j;
                ans += r-l;
            }
        }
        return ans;
    }
};
  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值