[解题报告]《算法零基础100讲》(第32讲) 多维枚举(二) - 进阶

目录

零、写在前面

一、主要知识点

        1.hash降维

二、课后习题 

1385. 两个数组间的距离值

1291. 顺次数

2048. 下一个更大的数值平衡数

349. 两个数组的交集

349. 两个数组的交集

1566. 重复至少 K 次且长度为 M 的模式

2025. 分割数组的最多方案数

写在最后


零、写在前面

        今天是打卡的第31天,今天的难度还行。知识点在:

《算法零基础100讲》(第32讲) 多维枚举(二) - 进阶https://blog.csdn.net/WhereIsHeroFrom/article/details/121449197icon-default.png?t=LA92https://blog.csdn.net/WhereIsHeroFrom/article/details/121449197


一、主要知识点

        1.hash降维

比英雄哥的代码改进了亿点点,就是不用每次都重设hash数组,考虑到c+1,那么b的可选项多了一个,枚举这一个多选项加入hash表就可以了。直接复杂度从O(n^3)变成了O(n^2)。

int countQuadruplets(int* nums, int n){
    int a, b, c, d;
    int hash[201];
    int sum = 0;
    memset(hash, 0, sizeof(hash));   
    for(c = 2; c < n; ++c) {
        b = c - 1;  //本次b新加了一个可选项
        for(a = b - 1; a >= 0; a--) 
            hash[ nums[a] + nums[b] ]++; 

        for(d = c+1; d < n; ++d) 
            if( nums[d] - nums[c] >= 0 ) 
                sum += hash[ nums[d] - nums[c] ];
    }
    return sum;
}

二、课后习题 

1385. 两个数组间的距离值

1385. 两个数组间的距离值https://leetcode-cn.com/problems/find-the-distance-value-between-two-arrays/

题目描述

给你两个整数数组 arr1 , arr2 和一个整数 d ,请你返回两个数组之间的 距离值 。

「距离值」 定义为符合此距离要求的元素数目:对于元素 arr1[i] ,不存在任何元素 arr2[j] 满足 |arr1[i]-arr2[j]| <= d 。

思路

先将arr2进行排序,然后在判断arr1元素符不符合要求的时候使用双指针,可以将复杂度降低。

int cmp(int *a,int *b){
    return *a > *b;
}
int findTheDistanceValue(int* arr1, int arr1Size, int* arr2, int arr2Size, int d){
    qsort(arr2,arr2Size,sizeof(int),cmp);
    int ans = 0;
    for(int i = 0;i < arr1Size;i++){
        int low = 0,high = arr2Size - 1;
        while(low < high){        //找到最接近arr[i]的两个数字
            int mid = (low + high) / 2;
            if(arr2[mid] > arr1[i]) high = mid;
            else low = mid + 1;
        }
        if((low < 1 ||abs(arr1[i] - arr2[low - 1]) > d) && (low > arr2Size -1||abs(arr1[i] - arr2[low]) > d))   ans++;//判断
    }
    return ans;
}

1291. 顺次数

1291. 顺次数https://leetcode-cn.com/problems/sequential-digits/

题目描述

我们定义「顺次数」为:每一位上的数字都比前一位上的数字大 1 的整数。

请你返回由 [low, high] 范围内所有顺次数组成的 有序 列表(从小到大排序)。

思路

这道题把,打表!打表yyds!

int cmp(int *a,int *b){return *a > *b;}
int* sequentialDigits(int low, int high, int* returnSize){
    int *ans = malloc(sizeof(int)*45);*returnSize = 0;
    int f[46],fsize= 0;
    for(int i = 1;i <= 9;i++){//生成所有的顺次数
        int temp = 0;
        for(int j = i;j <= 9;j++){
            temp *= 10;
            temp += j;
            f[fsize++] = temp;
        }
    }
    qsort(f,fsize,sizeof(int),cmp);//排序
    for(int i = 0;i < 45;i++)
        if(f[i] >= low && f[i] <= high) ans[(*returnSize)++] = f[i];
    return ans;
}

2048. 下一个更大的数值平衡数

2048. 下一个更大的数值平衡数https://leetcode-cn.com/problems/next-greater-numerically-balanced-number/

题目描述

如果整数  x 满足:对于每个数位 d ,这个数位 恰好 在 x 中出现 d 次。那么整数 x 就是一个 数值平衡数 。

给你一个整数 n ,请你返回 严格大于 n 的 最小数值平衡数 。

思路

这题就暴力枚举,枚举所有大于n的元素,判断是否满足条件。

bool isBalanced(int num){    //判断是否满足条件
    int hash[10] = {0};
    while(num){
        hash[num % 10] ++;
        num /= 10;
    }
    for(int i = 0;i < 10;i++){
        if(hash[i] != 0 && hash[i] != i)    return false;
    }
    return true;
}
int nextBeautifulNumber(int n){
    while(1){
        n++;
        if(isBalanced(n))   return n;
    }
    return -1;
}

349. 两个数组的交集

349. 两个数组的交集https://leetcode-cn.com/problems/intersection-of-two-arrays/

题目描述

给定两个数组,编写一个函数来计算它们的交集。

思路

排序之后双指针。你们看得懂的。

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

int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
    qsort(nums1, nums1Size, sizeof(int), cmp);
    qsort(nums2, nums2Size, sizeof(int), cmp);
    *returnSize = 0;
    int low = 0, high = 0;
    int* ans = malloc(sizeof(int) * (nums1Size > nums2Size ? nums1Size :nums2Size));
    //申请两个中较短的数组长度就好了
    while (low < nums1Size && high < nums2Size) {
        if (nums1[low] == nums2[high]) {
            //去重插入
            if (!(*returnSize) || nums1[low] != ans[(*returnSize) - 1])
                ans[(*returnSize)++] = nums1[low];
            low++;
            high++;
        } 
        else if (nums1[low] < nums2[high]) {
            low++;
        }
        else high++;
    }
    return ans;
}

349. 两个数组的交集

349. 两个数组的交集https://leetcode-cn.com/problems/intersection-of-two-arrays/

题目描述

给你一个 下标从 0 开始 的整数数组 nums ,返回满足下述条件的 不同 四元组 (a, b, c, d) 的 数目 :

nums[a] + nums[b] + nums[c] == nums[d] ,且
a < b < c < d

思路

就是例题。其实就是固定c之后把a+b的值固定为hash表,同时更新hash表将复杂度降到O(n^2)。

int countQuadruplets(int* nums, int n){
    int a, b, c, d;
    int hash[201];
    int sum = 0;
    memset(hash, 0, sizeof(hash));//初始化hash   
    for(c = 2; c < n; ++c) {
        b = c - 1;  //本次b新加了一个可选项
        for(a = b - 1; a >= 0; a--) 
            hash[ nums[a] + nums[b] ]++; 

        for(d = c+1; d < n; ++d) 
            if( nums[d] - nums[c] >= 0 ) 
                sum += hash[ nums[d] - nums[c] ];
    }
    return sum;
}

1566. 重复至少 K 次且长度为 M 的模式

1566. 重复至少 K 次且长度为 M 的模式https://leetcode-cn.com/problems/detect-pattern-of-length-m-repeated-k-or-more-times/

题目描述

给你一个正整数数组 arr,请你找出一个长度为 m 且在数组中至少重复 k 次的模式。

模式 是由一个或多个值组成的子数组(连续的子序列),连续 重复多次但 不重叠 。 模式由其长度和重复次数定义。

如果数组中存在至少重复 k 次且长度为 m 的模式,则返回 true ,否则返回  false 。

思路

这个其实因为连续出现k次且不重叠,就可以按照取模来运算了。

bool containsPattern(int* arr, int arrSize, int m, int k){
    for(int i = 0;i <= arrSize - m *k;++i){
        int temp;
        for(temp = 0;temp < m * k;++temp){
            if(arr[i+temp] != arr[i + temp%m])   break;//重复出现k次的判断
        }
        if(temp == m*k) return true;
    }
    return false;
}

2025. 分割数组的最多方案数

2025. 分割数组的最多方案数https://leetcode-cn.com/problems/maximum-number-of-ways-to-partition-an-array/

题目描述

给你一个下标从 0 开始且长度为 n 的整数数组 nums 。分割 数组 nums 的方案数定义为符合以下两个条件的 pivot 数目:

1 <= pivot < n
nums[0] + nums[1] + ... + nums[pivot - 1] == nums[pivot] + nums[pivot + 1] + ... + nums[n - 1]
同时给你一个整数 k 。你可以将 nums 中 一个 元素变为 k 或 不改变 数组。

请你返回在 至多 改变一个元素的前提下,最多 有多少种方法 分割 nums 使得上述两个条件都满足。

思路

这个题也算是空间换时间了。我一共用了三个hash表,其中一个维护的是所有的前缀和,两个用于维护i左边和右边的分割差。

我们可以发现 如果修改的是分割位置的左边的值 那么就有之前的差 左 - 右 = d

若修改的是分割位置的右边的值 那么就有之前的差 左 - 右 = -d

其中d = nums[i] - k 就是改变造成了多大的改变。

然后就是用两个是为了分别去判断两边的值。避免发生影响。

int cha1[200010];
int cha2[200010];
int waysToPartition(int* nums, int numsSize, int k){
    long long temp[numsSize];
    temp[0] = nums[0];
    for(int i = 1;i < numsSize;i++)   temp[i] = temp[i-1] + nums[i];//求前缀和
    int ans = 0;
    for(int i = 0;i < numsSize;i++){    //hash初始化用到什么初始化什么
        int d = nums[i] - k;
        if(d <=100000 && d>=-100000){
            cha1[d + 100000] = 0;
            cha1[100000 - d] = 0;
            cha2[d + 100000] = 0;
            cha2[100000 - d] = 0;
        }
    }
    for(int i = 0;i < numsSize - 1;i++){//第一次判断 顺带更新cha1 cha1表示更改值右边的hash表
        long long d = 2*temp[i] - temp[numsSize - 1];
        if(d <=100000 && d>=-100000)    cha1[d+100000]++;
        if(d == 0) ans++;
    }
    for(int i = 0;i < numsSize;i++){
        if(i){//更新hash表 i更改会造成cha1 和cha2 的改变
            int k = 2*temp[i - 1] - temp[numsSize - 1];
            if(k <=100000 && k>=-100000)    {cha1[k+100000]--;cha2[k+100000]++;}
        }
        int now = 0,d = nums[i] - k;
        if(d <=100000 && d>=-100000){//根据公式进行判断
            now += cha1[d + 100000];
            now += cha2[100000 - d];
        }
        if(now > ans) ans = now;
    }
    return ans;
}

写在最后

今天题不多,并且今天的复习效果也还不错,接下来的两周,都会保存这样的节奏,题解是不会断更的,希望各位看官都能满意。感谢大家的关注!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XingleiGao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值