Leetcode第239场周赛 总结 + 补题

到目标元素的最小距离

Leetcode 5746 到目标元素的最小距离题目链接
题目描述
给你一个整数数组 n u m s nums nums (下标从 0 开始 计数)以及两个整数 t a r g e t target target s t a r t start start ,请你找出一个下标 i i i,满足 n u m s [ i ] nums[i] nums[i] == t a r g e t target target a b s ( i − s t a r t ) abs(i - start) abs(istart) 最小化 。注意: a b s ( x ) abs(x) abs(x) 表示 x x x 的绝对值。
返回 a b s abs abs( i i i - s t a r t start start) 。

题目数据保证 t a r g e t target target 存在于 n u m s nums nums 中。

示例 1

输入:nums = [1,2,3,4,5], target = 5, start = 3
输出:1
解释:nums[4] = 5 是唯一一个等于 target 的值,所以答案是 abs(4 - 3) = 1 

示例 2

输入:nums = [1], target = 1, start = 0
输出:0
解释:nums[0] = 1 是唯一一个等于 target 的值,所以答案是 abs(0 - 0) = 1 

提示:
1 ≤ n u m s . l e n g t h ≤ 1000 1 \leq nums.length \leq 1000 1nums.length1000
1 ≤ n u m s [ i ] ≤ 1 0 4 1 \leq nums[i] \leq 10^4 1nums[i]104
0 ≤ s t a r t < n u m s . l e n g t h 0 \leq start < nums.length 0start<nums.length
t a r g e t target target 存在于 n u m s nums nums

class Solution {
public:
    int getMinDistance(vector<int>& nums, int target, int start) {
        int res = 1100;
        int len = nums.size();
        for(int i = 0; i < len; i++)
        {
            if(nums[i] == target)
                res = min(res, abs(i-start));
        }
        return res;
    }
};

将字符串拆分为递减的连续值

Leetcode 5747 将字符串拆分为递减的连续值
题目描述
给你一个仅由数字组成的字符串 s s s
请你判断能否将 s s s 拆分成两个或者多个非空子字符串 ,使子字符串的数值 按降序排列,且每两个相邻子字符串的数值之差等于 1 。

例如,字符串 s s s = “0090089” 可以拆分成 [ " 0090 " , " 089 " ] ["0090", "089"] ["0090","089"],数值为 [ 90 , 89 ] [90,89] [90,89]。这些数值满足按降序排列,且相邻值相差 1 ,这种拆分方法可行。

另一个例子中,字符串 s s s = “001” 可以拆分成 [ " 0 " , " 01 " ] ["0", "01"] ["0","01"] [ " 00 " , " 1 " ] ["00", "1"] ["00","1"] [ " 0 " , " 0 " , " 1 " ] ["0", "0", "1"] ["0","0","1"] 。然而,所有这些拆分方法都不可行,因为对应数值分别是 [ 0 , 1 ] [0,1] [0,1] [ 0 , 1 ] [0,1] [0,1] [ 0 , 0 , 1 ] [0,0,1] [0,0,1] ,都不满足按降序排列的要求。
如果可以按要求拆分 s s s ,返回 t r u e true true;否则,返回 f a l s e false false
子字符串 是字符串中的一个连续字符序列。

示例 1

输入:s = "1234"
输出:false
解释:不存在拆分 s 的可行方法。

示例 2

输入:s = "050043"
输出:true
解释:s 可以拆分为 ["05", "004", "3"] ,对应数值为 [5,4,3] 。
满足按降序排列,且相邻值相差 1 。

示例 3

输入:s = "9080701"
输出:false
解释:不存在拆分 s 的可行方法。

示例 4

输入:s = "10009998"
输出:true
解释:s 可以拆分为 ["100", "099", "98"] ,对应数值为 [100,99,98] 。
满足按降序排列,且相邻值相差 1 。

提示:
1 ≤ s . l e n g t h ≤ 20 1 \leq s.length \leq 20 1s.length20
s s s 仅由数字组成

解题思路
1.删除前导 0.
2.枚举的数的长度顶多是 s . s i z e ( ) + 1 > > 1 s.size() + 1 >> 1 s.size()+1>>1
3.当 f i r s t = = s e c o n d + 1 first == second + 1 first==second+1 时, f i s r t = s e c o n d , s e c o n d = 0 fisrt = second,second = 0 fisrt=secondsecond=0,当枚举到区间右端点末端时,当前长度的数是个合法方案。
4.当 s e c o n d > = f i s r t second >= fisrt second>=fisrt 时当前长度的枚举长度不存在合法方案,枚举长度加 1,然后重新计算 f i r s t first first,重新寻找 s e c o n d second second
5.注意处理当 f i r s t = = 0 first == 0 first==0 时,区间右端点都是 0 也是一种合法方案。

class Solution {
public:
    string s = "";
    typedef long long ll;
    bool splitString(string _s) {
        int len = _s.size();
        int pos = 0;
        for(int i = 0; i < len; i++)
            if(_s[i] == '0') pos++;
            else break;
        s = _s.substr(pos,len-pos);
        if(s.size() <= 1) return false;
        
        int n = s.size() + 1 >> 1;
        for(int i = 1; i <= n; i++)
        {
            ll first = 0, second = 0;
            for(int k = 0; k < i; k++) first = first*10 + (ll)(s[k] - '0');


            for(int j = i; j < s.size(); j++)
            {
                while(second < first && j < s.size())  
                {
                    second = second * 10 + (ll)(s[j++] - '0');
                    if(second + 1 == first)
                    {
                        first = second;
                        second = 0;
                        if(first == 0) while(s[j] == '0' && j < s.size()) j++;
                        if(j == s.size()) return true;
                    }
                    else if(second >= first) j = s.size() + 1;
                }
            }
        }
        return false;
    }
};

解题思路2:状态压缩
1.字符串长度为 20,最多划分成 19 段,枚举 1 ~ 2 19 2^{19} 219,数据类型用 u n s i g n e d unsigned unsigned l o n g long long l o n g long long,如果数据溢出是对 2 64 2^{64} 264 取模。
2. 2 19 2^{19} 219状态方案中寻找只要有一个状态满足题意就返回 t r u e true true,否则返回 f a l s e false false
3.每个状态枚举每一位,如果是 1 就是一条分割线,否则就不是。

class Solution {
public:
    typedef unsigned long long ull;
    bool splitString(string s) {
        int n = s.size();
        ull m = 1 << (ull)(n-1);
        for(ull i = 1; i <= m; i++)
        {
            bool st = true;
            ull pre = -1, now = s[0] - '0';
            for(int j = 0; j < n - 1; j++)
            {
                if(i >> j & 1)
                {
                    if(pre != -1 && now + 1 != pre)
                    {
                        st = false;
                        break;
                    }
                    pre = now;
                    now = s[j+1] - '0';
                }
                else now = now*10 + s[j+1] - '0';
            }
            if(pre != now + 1) st = false;
            if(st) return true;
        }
        return false;
    }
};

邻位交换的最小次数

Leetcode 5749 邻位交换的最小次数
题目描述
给你一个表示大整数的字符串 n u m num num ,和一个整数 k k k

如果某个整数是 n u m num num 中各位数字的一个排列且它的值大于num ,则称这个整数为妙数 。可能存在很多妙数,但是只需要关注 值最小 的那些。

例如, n u m num num = “5489355142”:
第 1 个最小妙数是 “5489355214”
第 2 个最小妙数是 “5489355241”
第 3 个最小妙数是 “5489355412”
第 4 个最小妙数是 “5489355421”
返回要得到第 k k k 个 最小妙数 需要对 n u m num num 执行的 相邻位数字交换的最小次数 。
测试用例是按存在第 k k k 个最小妙数而生成的。

示例 1

输入:num = "5489355142", k = 4
输出:2
解释:第 4 个最小妙数是 "5489355421" ,要想得到这个数字:
- 交换下标 7 和下标 8 对应的位:"5489355142" -> "5489355412"
- 交换下标 8 和下标 9 对应的位:"5489355412" -> "5489355421"

示例 2

输入:num = "11112", k = 4
输出:4
解释:第 4 个最小妙数是 "21111" ,要想得到这个数字:
- 交换下标 3 和下标 4 对应的位:"11112" -> "11121"
- 交换下标 2 和下标 3 对应的位:"11121" -> "11211"
- 交换下标 1 和下标 2 对应的位:"11211" -> "12111"
- 交换下标 0 和下标 1 对应的位:"12111" -> "21111"

示例 3

输入:num = "00123", k = 1
输出:1
解释:第 1 个最小妙数是 "00132" ,要想得到这个数字:
- 交换下标 3 和下标 4 对应的位:"00123" -> "00132"

提示:
2 ≤ n u m . l e n g t h ≤ 1000 2 \leq num.length \leq 1000 2num.length1000
1 ≤ k ≤ 1000 1 \leq k \leq 1000 1k1000
n u m num num 仅由数字组成

解题思路

  1. n e x t _ p e r m u t a t i o n next\_permutation next_permutation 求第 k k k 个排列。
  2. 原序列每个数有个下标,即 [0…n-1],在第 k k k 个排列中每个字符对应原来字符的下标一一对应。
  3. 求一下逆序对数量就是最后的交换次数。
class Solution {
public:
    int getMinSwaps(string num, int k) {
        string s = num;
        while(k--) next_permutation(s.begin(),s.end());

        int n = s.size();
        int cnt[10] = {0}, a[1007] = {0};

        for(int i = 0; i < n; i++)
        {
            int pre = num[i] - '0';
            int out = 0;
            cnt[pre]++;
            for(int j = 0; j < n; j++)
                if(s[j] - '0' == pre && ++out == cnt[pre])
                    a[j] = i, j = n;
        }

        int ans = 0;
        for(int i = 0; i < n; i++)
            for(int j = i + 1; j < n; j++)
                if(a[i] > a[j]) ans++;
        return ans;
    }
};

包含每个查询的最小区间

Leetcode 1851 包含每个查询的最小区间
题目描述
给你一个二维整数数组 i n t e r v a l s intervals intervals ,其中 i n t e r v a l s [ i ] intervals[i] intervals[i] = [ l e f t i , r i g h t i ] [left_i, right_i] [lefti,righti] 表示第 i i i 个区间开始于 l e f t i left_i lefti 、结束于 r i g h t i right_i righti(包含两侧取值,闭区间)。区间的 长度 定义为区间中包含的整数数目,更正式地表达是 r i g h t i right_i righti - l e f t i left_i lefti + 1 。

再给你一个整数数组 q u e r i e s queries queries。第 j j j 个查询的答案是满足 l e f t i left_i lefti ≤ \leq q u e r i e s [ j ] queries[j] queries[j] ≤ \leq r i g h t i right_i righti 的长度最小区间 i i i 的长度 。如果不存在这样的区间,那么答案是 -1 。

以数组形式返回对应查询的所有答案。

示例 1

输入:intervals = [[1,4],[2,4],[3,6],[4,4]], queries = [2,3,4,5]
输出:[3,3,1,4]
解释:查询处理如下:
- Query = 2 :区间 [2,4] 是包含 2 的最小区间,答案为 4 - 2 + 1 = 3 。
- Query = 3 :区间 [2,4] 是包含 3 的最小区间,答案为 4 - 2 + 1 = 3 。
- Query = 4 :区间 [4,4] 是包含 4 的最小区间,答案为 4 - 4 + 1 = 1 。
- Query = 5 :区间 [3,6] 是包含 5 的最小区间,答案为 6 - 3 + 1 = 4 。

示例 2

输入:intervals = [[2,3],[2,5],[1,8],[20,25]], queries = [2,19,5,22]
输出:[2,-1,4,6]
解释:查询处理如下:
- Query = 2 :区间 [2,3] 是包含 2 的最小区间,答案为 3 - 2 + 1 = 2 。
- Query = 19:不存在包含 19 的区间,答案为 -1 。
- Query = 5 :区间 [2,5] 是包含 5 的最小区间,答案为 5 - 2 + 1 = 4 。
- Query = 22:区间 [20,25] 是包含 22 的最小区间,答案为 25 - 20 + 1 = 6 

提示:
1 ≤ i n t e r v a l s . l e n g t h ≤ 1 0 5 1 \leq intervals.length \leq10^5 1intervals.length105
1 ≤ q u e r i e s . l e n g t h ≤ 1 0 5 1 \leq queries.length \leq 10^5 1queries.length105
q u e r i e s [ i ] . l e n g t h = = 2 queries[i].length == 2 queries[i].length==2
1 ≤ l e f t i < = r i g h t i ≤ 1 0 7 1 \leq lefti <= righti \leq 10^7 1lefti<=righti107
1 ≤ q u e r i e s [ j ] ≤ 1 0 7 1 \leq queries[j] \leq 10^7 1queries[j]107

离散化+并查集

class Solution {
public:
    vector<int> v, fa, w;
    //并查集初始化
    void Init(int x){
        for(int i = 0; i <= x; i++) fa[i] = i, w[i] = -1;
    }
    //路径压缩
    int find(int x){
        return fa[x] = (fa[x] == x ? fa[x] : find(fa[x]));
    }
    //查询
    int Query(int x){
        return lower_bound(v.begin(),v.end(),x) - v.begin();
    }
    vector<int> minInterval(vector<vector<int>>& intervals, vector<int>& queries) {
        for(auto& x : intervals) v.push_back(x[0]), v.push_back(x[1]);
        for(auto x : queries) v.push_back(x);
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        
        sort(intervals.begin(),intervals.end(),
        [](vector<int>& x, vector<int>& y){
            return x[1] - x[0] < y[1] - y[0];
        });

        int n = v.size();
        fa.resize(n+7); w.resize(n+7);
        Init(n);
        for(auto& x : intervals){
            int val = x[1] - x[0] + 1;
            int l = Query(x[0]), r = Query(x[1]);
            while(find(l) <= r){
                l = find(l);
                w[l] = val;
                fa[l] = l + 1;
            }
        }
        vector<int> ans;
        for(auto x : queries) ans.push_back(w[Query(x)]);
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

幸愉聊信奥

谢谢亲的支持,我会继续努力啦~

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

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

打赏作者

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

抵扣说明:

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

余额充值