【LeetCode 算法竞赛】 第 305 场周赛

145 阅读2分钟

6136. 算术三元组的数目

给你一个下标从 0 开始、严格递增 的整数数组 nums 和一个正整数 diff 。如果满足下述全部条件,则三元组 (i, j, k) 就是一个 算术三元组 :

  • i < j < k ,
  • nums[j] - nums[i] == diff 且
  • nums[k] - nums[j] == diff 返回不同 算术三元组 的数目。

示例1

输入:nums = [0,1,4,6,7,10], diff = 3
输出:2
解释:
(1, 2, 4) 是算术三元组:7 - 4 == 34 - 1 == 3 。
(2, 4, 5) 是算术三元组:10 - 7 == 37 - 4 == 3

示例2

输入:nums = [4,5,6,7,8,9], diff = 2
输出:2
解释:
(0, 2, 4) 是算术三元组:8 - 6 == 26 - 4 == 2 。
(1, 3, 5) 是算术三元组:9 - 7 == 27 - 5 == 2

示例3

输入: words = ["blue","green","bu"]
输出: []

思路

  • 本题目采用暴力求解,通过三层循环,寻找符合条件(i < j < k,nums[j] - nums[i] == diff,nums[k] - nums[j] == diff)的三元组。

解法

func arithmeticTriplets(nums []int, diff int) int {
    count:=0
    for i:=0;i<len(nums);i++{
        for j:=i+1;j<len(nums);j++{
            for k:=j+1;k<len(nums);k++{
                if nums[j]-nums[i]==diff && nums[k]-nums[j]==diff{
                    count++
                    continue
                }
            }
        }
    }
    return count
}

结果

image.png

思路

  • 相比上述一题的优化点在于:以空间换时间的思想,使用map,省去了三层循环。

解法

func arithmeticTriplets(nums []int, diff int) int {
    count:=0
    dic:=map[int]bool{}
    for i:=0;i<len(nums);i++{
        dic[nums[i]]=true
    }
    for i:=0;i<len(nums);i++{
        if dic[nums[i]-diff] && dic[nums[i]+diff]{
            count++
        }
    }
    return count
}

结果

image.png


6137. 检查数组是否存在有效划分

给你一个下标从 0 开始的整数数组 nums ,你必须将数组划分为一个或多个连续子数组。

如果获得的这些子数组中每个都能满足下述条件 之一 ,则可以称其为数组的一种有效划分:

  • 子数组 恰 由 2 个相等元素组成,例如,子数组 [2,2] 。
  • 子数组 恰 由 3 个相等元素组成,例如,子数组 [4,4,4] 。
  • 子数组 恰 由 3 个连续递增元素组成,并且相邻元素之间的差值为 1 。例如,子数组 [3,4,5] ,但是子数组 [1,3,5] 不符合要求。 如果数组 至少 存在一种有效划分,返回 true ,否则,返回 false 。

示例1

输入:nums = [4,4,4,5,6]
输出:true
解释:数组可以划分成子数组 [4,4][4,5,6] 。
这是一种有效划分,所以返回 true 。

示例2

输入: nums = [1,1,1,2]
输出: false
解释: 该数组不存在有效划分。

思路

  • 自己做这道题的时候,使用的是回溯,但是超时了。。。因此,学习了别人的解法。
  • 本题中“划分”说明是存在子问题,那么想到子问题,则一般采用动态规范的方法求解。
  • 定义dp[i+1]表示nums[0]到nums[i]这些元素能否被有效划分。
  • dp[i+1]=true,如下条件满足一个即可:
  • 当i>0,dp[i-1]=true,nums[i-1]=nums[i]
  • 当i>1,dp[i-2]=true,nums[i]=nums[i-1],nums[i]=nums[i-2] 或者 nums[i]=nums[i-1]+1,nums[i]=nums[i-2]+1

解法

func validPartition(nums []int) bool {
    dp:=make([]bool,len(nums)+1)
    dp[0]=true
    for i:=0;i<len(nums);i++{
        if i>0 && dp[i-1] && nums[i]==nums[i-1] || i>1 && dp[i-2] && ( nums[i]==nums[i-1] && nums[i]==nums[i-2] || nums[i]==nums[i-1]+1 && nums[i]==nums[i-2]+2){
            dp[i+1]=true
        }
    }
    return dp[len(nums)]
}

结果

image.png


6138. 最长理想子序列

给你一个由小写字母组成的字符串 s ,和一个整数 k 。如果满足下述条件,则可以将字符串 t 视作是理想字符串

  • t 是字符串 s 的一个子序列。
  • t 中每两个相邻 字母在字母表中位次的绝对差值小于或等于 k 。 返回最长 理想字符串的长度。

字符串的子序列同样是一个字符串,并且子序列还满足:可以经由其他字符串删除某些字符(也可以不删除)但不改变剩余字符的顺序得到。

注意:字母表顺序不会循环。例如,'a' 和 'z' 在字母表中位次的绝对差值是 25 ,而不是 1 。

示例1

输入:s = "acfgbd", k = 2
输出:4
解释:最长理想字符串是 "acbd" 。该字符串长度为 4 ,所以返回 4 。
注意 "acfgbd" 不是理想字符串,因为 'c''f' 的字母表位次差值为 3

示例2

输入: s = "abcd", k = 3
输出: 4
解释: 最长理想字符串是 "abcd" ,该字符串长度为 4 ,所以返回 4 。

思路

  • 本题是字符串类问题,字符串类问题,一般使用枚举的方法。
  • 子序列+相邻,一般使用dp方法解决。
  • 定义dp[i][c]表示前i个字符里面选一个末尾字符为c的理想字符串的最长长度。
  • dp[i][c]=max(dp[i-1][c])+1,其中c的选取范围为[c-k,c+k],当然也在[0,25]的范围内。
  • 进一步优化,因为dp[i]从dp[i-1]中选取,因此,可以降维处理,将二维转为一维。

解法

func longestIdealString(s string, k int) int {
    dp:=make([]int,26)
    for _,c:=range s{ // 字符串问题,需要遍历
        c:=int(c-'a') // 将当前字符转为[0,25]中的一个
        for j:=max(0,c-k);j<=min(25,c+k);j++{ // c的选取范围为[c-k,c+k],找一个最长的
            dp[c]=max(dp[j],dp[c])
        }
        dp[c]++ // 将c的1进去
    }
    count:=0
    for _,v:=range dp{
        count=max(count,v)
    }
    return count
}

func max(a,b int)int{
    if a>b{
        return a
    }
    return b
}

结果

image.png