【Leetcode Sheet】Weekly Practice 17

Leetcode Test

2216 美化数组的最少删除数(11.21)

给你一个下标从 0 开始的整数数组 nums ,如果满足下述条件,则认为数组 nums 是一个 美丽数组

  • nums.length 为偶数
  • 对所有满足 i % 2 == 0 的下标 inums[i] != nums[i + 1] 均成立

注意,空数组同样认为是美丽数组。

你可以从 nums 中删除任意数量的元素。当你删除一个元素时,被删除元素右侧的所有元素将会向左移动一个单位以填补空缺,而左侧的元素将会保持 不变

返回使 nums 变为美丽数组所需删除的 最少 元素数目*。*

提示:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 105

【贪心】

int minDeletion(int* nums, int numsSize) {
    //美丽数组要求
    //1:偶数下标i,nums[i]!=nums[i+1]
    //2:len(new-nums)是偶数

    int n=numsSize,left=0,right=1,cnt=0;
    //left记录偶数下标,right探索下一个数,cnt记录删除的个数

    while(right<n){
        if(nums[right]!=nums[left]){
            //符合等式条件
            left=right+1;
            right=left+1;
        }
        else{
            //不符合等式条件
            right++;
            cnt++;
        }
    }

    if(left<n){
        //如果最后剩余奇数个数字,则需要删除一个
        cnt++;
    }
    
    return cnt;
}

【cpp极简版本】宫水三叶

2216. 美化数组的最少删除数 - 力扣(LeetCode)

class Solution {
public:
    int minDeletion(vector<int>& nums) {
        int n = nums.size(), cnt = 0;
        for (int i = 0; i < n; i++) {
            if ((i - cnt) % 2 == 0 && i + 1 < n && nums[i] == nums[i + 1]) {
                //如果当前下标是偶数,且下一个下标存在数组中,且当前num等于下一个num
                //则不符合条件,cnt自增
                cnt++;
            }
        }
        return (n - cnt) % 2 != 0 ? cnt + 1 : cnt;
    }
};
/*
1:每次删除元素,剩余元素都会往前移动,因此当前下标为i-cnt
2:处理 nums 过程中,若当前下标为偶数,且与下一位置元素相同,那么当前元素需被删除,令 cnt 自增
3:最终数组长度为 n−cnt,若长度为奇数,需要再额外删除结尾元素(cnt 再加一)
*/

2304 网格中的最小路径代价(11.22)

给你一个下标从 0 开始的整数矩阵 grid ,矩阵大小为 m x n ,由从 0m * n - 1 的不同整数组成。你可以在此矩阵中,从一个单元格移动到 下一行 的任何其他单元格。如果你位于单元格 (x, y) ,且满足 x < m - 1 ,你可以移动到 (x + 1, 0), (x + 1, 1), …, (x + 1, n - 1) 中的任何一个单元格。注意: 在最后一行中的单元格不能触发移动。

每次可能的移动都需要付出对应的代价,代价用一个下标从 0 开始的二维数组 moveCost 表示,该数组大小为 (m * n) x n ,其中 moveCost[i][j] 是从值为 i 的单元格移动到下一行第 j 列单元格的代价。从 grid 最后一行的单元格移动的代价可以忽略。

grid 一条路径的代价是:所有路径经过的单元格的 值之和 加上 所有移动的 代价之和 。从 第一行 任意单元格出发,返回到达 最后一行 任意单元格的最小路径代价。

提示:

  • m == grid.length
  • n == grid[i].length
  • 2 <= m, n <= 50
  • grid 由从 0m * n - 1 的不同整数组成
  • moveCost.length == m * n
  • moveCost[i].length == n
  • 1 <= moveCost[i][j] <= 100

【记忆化搜索】dfs
d f s ( i , j ) = { g r i d [ i ] [ j ] i = 0 m i n k = 0 n − 1 ( d f s ( i − 1 , k ) + m o v e C o s t [ g r i d [ i − 1 ] [ k ] ] [ j ] + g r i d [ i ] [ j ] ) i > 0 dfs(i,j)=\begin{cases} grid[i][j]&i=0\\ min_{k=0}^{n-1}(dfs(i-1,k)+moveCost[grid[i-1][k]][j]+grid[i][j])&i>0 \end{cases} dfs(i,j)={grid[i][j]mink=0n1(dfs(i1,k)+moveCost[grid[i1][k]][j]+grid[i][j])i=0i>0
到达第 0 行的单元格,则代价是到达单元格的数值

到达第 i 行的单元格 (i, j) ,则代价是到达单元格 (i-1, k)的代价 + (i-1, k)到(i, j)的路径代价 + (i, j)的数值

int **memo=NULL;

//dfs(i,j)是从第0行的任意单元格出发,到达(i,j)单元格的最小路径代价
int dfs(int i,int j,int **grid,int **moveCost,int n){
    if(i==0) return grid[i][j];
    if(memo[i][j]>=0) return memo[i][j];//记忆了dfs
    
    int ret=INT_MAX;
    for(int k=0;k<n;k++){
        ret=fmin(ret,dfs(i-1,k,grid,moveCost,n)+moveCost[grid[i-1][k]][j]+grid[i][j]);
    }
    memo[i][j]=ret;
    return ret;
}

int minPathCost(int** grid, int gridSize, int* gridColSize, int** moveCost, int moveCostSize, int* moveCostColSize) {
    int m=gridSize,n=gridColSize[0];
    memo=(int**)malloc(sizeof(int*)*m);
    for(int i=0;i<m;i++){
        memo[i]=(int*)malloc(sizeof(int)*n);
        for(int j=0;j<n;j++){
            memo[i][j]=-1;
        }
    }

    int ret=INT_MAX;
    for(int j=0;j<n;j++){
        ret=fmin(ret,dfs(m-1,j,grid,moveCost,n));
    }
    for(int i=0;i<m;i++){
        free(memo[i]);
    }
    free(memo);
    return ret;
}

1410 HTML实体解析器(11.23)

「HTML 实体解析器」 是一种特殊的解析器,它将 HTML 代码作为输入,并用字符本身替换掉所有这些特殊的字符实体。

HTML 里这些特殊字符和它们对应的字符实体包括:

  • **双引号:**字符实体为 " ,对应的字符是 "
  • **单引号:**字符实体为 ' ,对应的字符是 '
  • **与符号:**字符实体为 & ,对应对的字符是 &
  • **大于号:**字符实体为 > ,对应的字符是 >
  • **小于号:**字符实体为 < ,对应的字符是 <
  • **斜线号:**字符实体为 ,对应的字符是 /

给你输入字符串 text ,请你实现一个 HTML 实体解析器,返回解析器解析后的结果。

提示:

  • 1 <= text.length <= 10^5
  • 字符串可能包含 256 个ASCII 字符中的任意字符。

【模拟】遍历1次并替换substring,使用strncmp函数进行子串的比较

char* entityParser(char* text) {
    char* ans = (char*)calloc(1, sizeof(char) * (strlen(text) + 1));
    char* p = ans;
    while (*text) 
        if      (*text != '&'                ) *(p ++) =     *(text ++) ;
        else if (!strncmp(text, """ , 6)) *(p ++) = '\"', text += 6;
        else if (!strncmp(text, "'" , 6)) *(p ++) = '\'', text += 6;
        else if (!strncmp(text, "&"  , 5)) *(p ++) = '&' , text += 5;
        else if (!strncmp(text, ">"   , 4)) *(p ++) = '>' , text += 4;
        else if (!strncmp(text, "<"   , 4)) *(p ++) = '<' , text += 4;
        else if (!strncmp(text, "⁄", 7)) *(p ++) = '/' , text += 7;
        else                                   *(p ++) =     *(text ++) ;
    return ans;
}

2824 统计和小于目标的下标对数目(11.24)

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

提示:

  • 1 <= nums.length == n <= 50
  • -50 <= nums[i], target <= 50

【双指针】

int cmp(void *a,void *b){
    return *(int*)a-*(int*)b;
}

int countPairs(int* nums, int numsSize, int target){
    int n=numsSize,cnt=0;
    qsort(nums,n,sizeof(int),cmp);
    for(int i=0,j=n-1;i<j;i++){
        while(i<j && nums[i]+nums[j]>=target){
            j--;
        }
        cnt+=j-i;
    }
    return cnt;
}

1457 二叉树中的伪回文路径(11.25)

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

请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。

提示:

  • 给定二叉树的节点数目在范围 [1, 105]
  • 1 <= Node.val <= 9

【dfs + hash】

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
int maxVal=10;

bool check(int *arr){
    int cnt=0;
    for(int i=0;i<maxVal;i++){
        if(arr[i]%2==1){
            cnt++;
        }
    }
    return cnt<=1;//如果有2个数字是奇数个,则肯定不可能是回文
}

int dfs(struct TreeNode *root,int *arr){
    if(root==NULL){
        return 0;
    }

    arr[root->val]++;//当前数字进入hash表
    int ret=0;
    if(root->left==NULL && root->right==NULL){
        //叶子节点
        if(check(arr)){
            ret=1;
        }
    }
    else{
        //中间的节点
        ret=dfs(root->left,arr)+dfs(root->right,arr);
        //递归左右子树
    }
    arr[root->val]--;//返回前撤销更新(回溯)
    return ret;
}

int pseudoPalindromicPaths (struct TreeNode* root) {
    int arr[maxVal];//hash table for value
    for(int i=0;i<maxVal;i++){
        arr[i]=0;
    }
    return dfs(root,arr);
}

828 统计子串中的唯一字符(11.26)

我们定义了一个函数 countUniqueChars(s) 来统计字符串 s 中的唯一字符,并返回唯一字符的个数。

例如:s = "LEETCODE" ,则其中 "L", "T","C","O","D" 都是唯一字符,因为它们只出现一次,所以 countUniqueChars(s) = 5

本题将会给你一个字符串 s ,我们需要返回 countUniqueChars(t) 的总和,其中 ts 的子字符串。输入用例保证返回值为 32 位整数。

注意,某些子字符串可能是重复的,但你统计时也必须算上这些重复的子字符串(也就是说,你必须统计 s 的所有子字符串中的唯一字符)。

提示:

  • 1 <= s.length <= 105
  • s 只包含大写英文字符

【变化量】828. 统计子串中的唯一字符 - 力扣(LeetCode)

int uniqueLetterString(char* s) {
    int n=strlen(s),ans=0;
    char c;
    for(c='A';c<='Z';c++){
        int flag1=-1,flag0=-1;
        //flag0上一次出现的下标
        //flag1上上一次出现的下标
        for(int i=0;i<n;i++){
            if(s[i]==c){
                flag1=flag0;
                flag0=i;
            }
            ans+=flag0-flag1;
        }
    }
    return ans;
}

907 子数组的最小值之和(11.27)

给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。

由于答案可能很大,因此 返回答案模 10^9 + 7

提示:

  • 1 <= arr.length <= 3 * 104
  • 1 <= arr[i] <= 3 * 104

【单调栈 / 动态规划】907. 子数组的最小值之和 - 力扣(LeetCode)

int sumSubarrayMins(int* arr, int arrSize) {
    long long ans = 0;
    long long mod = 1e9 + 7;//取余
    
    int monoStack[arrSize], dp[arrSize];
    int top = 0;
    for (int i = 0; i < arrSize; i++) {
        while (top > 0 && arr[monoStack[top - 1]] > arr[i]) {
            top--;
        }
        int k = top == 0 ? (i + 1) : (i - monoStack[top - 1]);
        dp[i] = k * arr[i] + (top == 0 ? 0 : dp[i - k]);
        ans = (ans + dp[i]) % mod;
        monoStack[top++] = i;
    }
    return ans;
}
/*
从左向右遍历数组并维护一个单调递增的栈,如果栈顶的元素大于等于当前元素 arr[i] 则弹出栈,此时栈顶的元素即为左边第一个小于当前值的元素

我们求出以当前值为最右且最小的子序列的长度 k ,根据上述递推公式求出 dp[i],最终的返回值即为∑dp[i]
*/

你可能感兴趣的:(Leetcode,Series,leetcode,哈希算法,算法)