• LeetCode双周赛#34


    5492. 分割字符串的方案数 #组合公式 #乘法原理 #区间分割

    题目链接

    题意

    给定01二进制串(s),可将(s)分割为三个非空 字符串(s_1,s_2,s_3),即((s_1+s_2+s_3=s))。现要你求出分割(s)的方案数,保证(s_1,s_2,s_3)中字符1的数目相同(对(1e9+7)取模),他们的长度不一定相等

    分析

    举个例子,01100011000101串,可以知道三个子串必须包含2个'1',我们观察到左子串的界限,可以如下划分:011|000110001010110|001100010101100|011000101011000|11000101,共四种划分方案;同理对于右子串,共有四种划分方案。当我们将左右子串由于中间子串的间隔,使得左右子串的确定不会相互影响,同时中间子串也相应确定下来,通过乘法原理,总方案数=左子串划分方案数( imes)右子串划分方案数=16。

    然而,别忘了诸如00000等只包含'0'的串,三子串的划分确定会相互影响,比如0|000|0000|0|0等,此时我们可以用高中所学排列组合的“挡板法”确定下来,即串长为(n),有(n-1)空位,可以插入两个挡板,即(C^2_{n-1})

    typedef long long ll;
    class Solution {
    private:
        int sum[100005];
        const int MOD = 1e9 + 7;
    public:
        int numWays(string s) {
            int n = s.size();
            for (int i = 0; i < n; i++) //利用前缀和统计1的数目
                sum[i + 1] = sum[i + 1 - 1] + (s[i] == '1');
            if(sum[n] == 0){ //特判全部为0的串
                ll ans = (ll)(n - 1) * (ll)(n - 2)  / (ll)2;
                return ans % MOD;
            }
            if(sum[n] % 3 != 0) return 0; //特判不含有3的整数倍的串,无法分割
            else{
                int diff = sum[n] / 3; //确定每个子串中的'1'数量
                ll a = 0, b = 0;
                for (int i = 1; i <= n - 2; i++) //确定左子串可拓展的长度
                    if(sum[i] == diff) a++;
                for (int i = n; i >= 2; i--) //确定右子串可拓展的长度
                    if(sum[n] - sum[i - 1] == diff) b++;
                ll ans = (a % MOD) * (b % MOD); //乘法原理
                return ans % MOD;
            }
        }
    };
    

    5493. 删除最短的子数组使剩余数组有序 #归并排序 #区间分割

    题目链接

    题意

    给定整数数组arr ,你需要删除一个子数组(原数组中连续的一个子序列,可以为空),使得arr中剩下的元素是 非递减的。现要你求出满足题目要求的最短子数组的长度。

    分析

    感谢@Liuyh思路。

    题目要求只能删除一个子数组,也就说子数组内部存在一小段是非递减的,但也必须要删去。一般地,我们一定是从数组中间部分进行删除(只删除左部分或只删除右部分属于退化情况),即[....|XXXX|....](X表示要删除的部分)。 删除中间的子数组(并不代表最终删除子数组),意味着剩余前后两个子数组都符合非递减,这一操作需要前后两个指针,分别将两数组边界确定下来。代码中左子数组为[0, lo],而右子数组为[hi, len-1]。比如原数组[1,2,3,10,4,2,3,5],确定出左子数组 [1,2,3,10]lo指向10,右子数组[2, 3, 5]。中间数组删除长度为(hi-lo+1)

    现在我们要合并上述两个子数组,需要用到归并排序的思想,一旦发现右子数组比左子数组还大的元素,其中一个元素给舍弃掉(无需考虑是删除左数组的还是右数组的)。举个例子:

    • [1,2,3,10][2,3,5]
    • 由于1<2,此时[2,3,10][2,3,5]
    • 由于2<=2,此时[3,10][2,3,5]
    • 由于3>2,舍弃右子数组的2,左子数组跳过3ans++,此时[10][3,5]
    • 由于10>3,舍弃10,得到 [][5],算法结束。
    class Solution {
    public:
        int findLengthOfShortestSubarray(vector<int>& arr) {
            int lo = 0, len = arr.size(), hi = arr.size() - 1;
            if(len == 1) return 0; //特判:一个元素就无需删除了
            while(lo + 1 < len && arr[lo] <= arr[lo + 1]) lo++;
            //开始分割,确定左区间的右端点,注意,是往后递推,这样能够保证lo最终指向的依然是非降左子序列的右端点
            if(lo + 1 == len) return 0; //说明原数组是个非降序列,直接返回0
            while(lo < hi &&  arr[hi - 1] <= arr[hi]) hi--;
            if(hi == lo && hi == 0) return len - 1; //说明原数组是完全递减区间
    
            int ans = hi - lo - 1; //统计分割出来的区间
            int i = 0, j = hi; 
    
            while(i <= lo && j < len){ //合并区间
                if(arr[i] <= arr[j]) i++; //以左序列为基准
                else if(arr[i] > arr[j]){ //因为题目要求从左至右递增
                    i++; j++;//注意!一定是两个指针同时前进,因为你不知道哪个子数组影响最终结果
                    ans++;
                }
            }
            return ans;
        }
    };
    

    5494. 统计所有可行路径 #记忆化搜索

    题目链接

    题意

    有若干个城市,第i个城市有location[i]值。给定你城市出发起点start、目的地城市finish、初始汽油总量fuel。每当你从城市j移动到城市i时,消耗的油量为|location[j] - location[i]|。你要保证剩余的油量非负,方能移动到其他城市。同时,你可以经过任意城市超过一次(包括startfinish)。现要你求出从startfinish可能路径的数目(需取模)

    分析

    观察到数据范围(fuelleq200)(locations.lenleq100),直接记忆化搜索即可。但注意,尽管你此时到达了finish城市,只要你油量足够,仍能够移动到其他城市。

    dp[i][j]表示当前起点为i,剩余油量j的总方案数。

    要注意,我直接把dp数组放到全局变量的时候,出锅了不少qaq,别忘了在类内要对这个全局变量初始化一下,同时记忆化搜索一般赋值为-1较为稳妥。

    typedef long long ll;
    int dp[105][205];
    const int MOD = 1e9 + 7;
    class Solution {
    public:
        int dfs(vector<int>& locations, int start, int finish, int fuel){
            if(dp[start][fuel] != -1) return dp[start][fuel];
            ll ans = 0; int len = locations.size();
            if(start == finish) ans++;
            for (int i = 0; i < len; i++){
                int diff = abs(locations[i] - locations[start]);
                if(start == i) continue;
                if(fuel - diff >= 0){
                    ans = (ans + dfs(locations, i, finish, fuel - diff)) % MOD;
                }
            }
            dp[start][fuel] = (ans % MOD);
            return dp[start][fuel];
        }
        int countRoutes(vector<int>& locations, int start, int finish, int fuel) {
            memset(dp, -1, sizeof(dp));
            return dfs(locations, start, finish, fuel);
        }
    };
    
  • 相关阅读:
    线段树专辑——hdu 1698 Just a Hook
    bool树的计算
    线段树专辑—— hdu 2795 Billboard
    数据库索引技术聚集索引和非聚集索引介绍(二)
    数据库索引技术—索引介绍 (一)
    AJAX核心对象 XMLHttpRequest 对象使用详解 (二)
    AJAX核心对象 XMLHttpRequest 对象使用详解 (一)
    输入框的提示信息(获得焦点和失去焦点时显示提示信息)
    不管你今天做了多少,加一点亮点总结今天的得分就是1.01。
    新情况——需要按照这个规定堆
  • 原文地址:https://www.cnblogs.com/J-StrawHat/p/13624385.html
Copyright © 2020-2023  润新知