【力扣周赛】第354场周赛

6889. 特殊元素平方和

题目描述

描述:给你一个下标从 1 开始、长度为 n 的整数数组 nums 。

对 nums 中的元素 nums[i] 而言,如果 n 能够被 i 整除,即 n % i == 0 ,则认为 num[i] 是一个 特殊元素 。

返回 nums 中所有 特殊元素 的 平方和 。

示例 1:

输入:nums = [1,2,3,4]
输出:21
解释:nums 中共有 3 个特殊元素:nums[1] ,因为 4 被 1 整除;nums[2] ,因为 4 被 2 整除;以及 nums[4] ,因为 4 被 4 整除。 
因此,nums 中所有元素的平方和等于 nums[1] * nums[1] + nums[2] * nums[2] + nums[4] * nums[4] = 1 * 1 + 2 * 2 + 4 * 4 = 21 。

示例 2:

输入:nums = [2,7,1,19,18,3]
输出:63
解释:nums 中共有 4 个特殊元素:nums[1] ,因为 6 被 1 整除;nums[2] ,因为 6 被 2 整除;nums[3] ,因为 6 被 3 整除;以及 nums[6] ,因为 6 被 6 整除。 
因此,nums 中所有元素的平方和等于 nums[1] * nums[1] + nums[2] * nums[2] + nums[3] * nums[3] + nums[6] * nums[6] = 2 * 2 + 7 * 7 + 1 * 1 + 3 * 3 = 63 。 

提示:

1 <= nums.length == n <= 50
1 <= nums[i] <= 50

解题思路

思路:注意细节,下标从1开始,是下标能被n整除,元素是特殊元素。

class Solution {
public:
    int sumOfSquares(vector<int>& nums) {
        int sum=0;
        int n=nums.size();
        for(int i=0;i<n;i++)
        {
            if(n%(i+1)==0)
                sum+=nums[i]*nums[i];
        }
        return sum;
    }
};

总结:简单模拟,注意审题,越是简单的题,越容易忽略细节 ,如果题意尚未搞清楚就直接下笔,很容易大方向都是错的。

6929. 数组的最大美丽值

题目描述

描述:给你一个下标从 0 开始的整数数组 nums 和一个 非负 整数 k 。

在一步操作中,你可以执行下述指令:

在范围 [0, nums.length - 1] 中选择一个 此前没有选过 的下标 i 。
将 nums[i] 替换为范围 [nums[i] - k, nums[i] + k] 内的任一整数。
数组的 美丽值 定义为数组中由相等元素组成的最长子序列的长度。

对数组 nums 执行上述操作任意次后,返回数组可能取得的 最大 美丽值。

注意:你 只 能对每个下标执行 一次 此操作。

数组的 子序列 定义是:经由原数组删除一些元素(也可能不删除)得到的一个新数组,且在此过程中剩余元素的顺序不发生改变。

示例 1:

输入:nums = [4,6,1,2], k = 2
输出:3
解释:在这个示例中,我们执行下述操作:
- 选择下标 1 ,将其替换为 4(从范围 [4,8] 中选出),此时 nums = [4,4,1,2] 。
- 选择下标 3 ,将其替换为 4(从范围 [0,4] 中选出),此时 nums = [4,4,1,4] 。
执行上述操作后,数组的美丽值是 3(子序列由下标 0 、1 、3 对应的元素组成)。
可以证明 3 是我们可以得到的由相等元素组成的最长子序列长度。

示例 2:

输入:nums = [1,1,1,1], k = 10
输出:4
解释:在这个示例中,我们无需执行任何操作。
数组 nums 的美丽值是 4(整个数组)。

提示:

1 <= nums.length <= 105
0 <= nums[i], k <= 105

解题思路

思路:将数组进行排序后,其实问题就转换为了在数组中找一个子数组,这个子数组可变的区间范围都有交集,其实可以转换为该子数组最大值减去最小值小于等于2*k即可。既然是找一个连续的子数组,其实就可以使用滑动窗口。滑动窗口可以选择同向双指针,其大致原理是首先left和right都指向初始为0,当未满足条件时右指针右移,当满足条件时左指针右移以寻求最优解。

思路:一开始的想法是,每个数只能选一次,那么直接判断每一个数可变的区间范围是否有交集不就可以了吗?如果将数组排序使得相邻的元素在一起岂不是更好计算?现在问题是如何计算这些数可变的区间范围是否有交集呢?

int maximumBeauty(vector<int>& nums, int k) 
{
    int n=nums.size();
    // 排序 子序列 = 连续子数组 
    sort(nums.begin(),nums.end());
    int ans=0;
    // 同向双指针 最大-最小 <=2*k
    for(int l=0,r=0;r<n;r++)
    {
      // 如果不满足则左指针右移
      while(nums[r]-nums[l]>2*k)
        l++;
      // 每次满足要求则求最大值
        ans=max(ans,r-l+1);
    }
    return ans;
}

总结:滑动窗口,同向双指针,移动右指针,当不满足要求时移动左指针直至满足要求,然后收集对应结果。

6927. 合法分割的最小下标

题目描述

描述:如果元素 x 在长度为 m 的整数数组 arr 中满足 freq(x) * 2 > m ,那么我们称 x 是 支配元素 。其中 freq(x) 是 x 在数组 arr 中出现的次数。注意,根据这个定义,数组 arr 最多 只会有 一个 支配元素。

给你一个下标从 0 开始长度为 n 的整数数组 nums ,数据保证它含有一个支配元素。

你需要在下标 i 处将 nums 分割成两个数组 nums[0, …, i] 和 nums[i + 1, …, n - 1] ,如果一个分割满足以下条件,我们称它是 合法 的:

0 <= i < n - 1
nums[0, …, i] 和 nums[i + 1, …, n - 1] 的支配元素相同。
这里, nums[i, …, j] 表示 nums 的一个子数组,它开始于下标 i ,结束于下标 j ,两个端点都包含在子数组内。特别地,如果 j < i ,那么 nums[i, …, j] 表示一个空数组。

请你返回一个 合法分割 的 最小 下标。如果合法分割不存在,返回 -1 。

示例 1:

输入:nums = [1,2,2,2]
输出:2
解释:我们将数组在下标 2 处分割,得到 [1,2,2] 和 [2] 。
数组 [1,2,2] 中,元素 2 是支配元素,因为它在数组中出现了 2 次,且 2 * 2 > 3 。
数组 [2] 中,元素 2 是支配元素,因为它在数组中出现了 1 次,且 1 * 2 > 1 。
两个数组 [1,2,2] 和 [2] 都有与 nums 一样的支配元素,所以这是一个合法分割。
下标 2 是合法分割中的最小下标。

示例 2:

输入:nums = [2,1,3,1,1,1,7,1,2,1]
输出:4
解释:我们将数组在下标 4 处分割,得到 [2,1,3,1,1] 和 [1,7,1,2,1] 。
数组 [2,1,3,1,1] 中,元素 1 是支配元素,因为它在数组中出现了 3 次,且 3 * 2 > 5 。
数组 [1,7,1,2,1] 中,元素 1 是支配元素,因为它在数组中出现了 3 次,且 3 * 2 > 5 。
两个数组 [2,1,3,1,1] 和 [1,7,1,2,1] 都有与 nums 一样的支配元素,所以这是一个合法分割。
下标 4 是所有合法分割中的最小下标。

示例 3:

输入:nums = [3,3,3,3,7,2,2]
输出:-1
解释:没有合法分割。

提示:

1 <= nums.length <= 105
1 <= nums[i] <= 109
nums 有且只有一个支配元素。

解题思路

思路:类似于众数对应的摩尔投票。可以使用两个umap,首先遍历一次数组,使用umap1记录元素以及对应出现的次数,接着再遍历一次数组,此时使用i作为分界点,使用umap2统计当前遍历到i时累计出现的元素以及对应出现的次数,再判断当前元素nums[i]在以i分割的两个子数组中是否都满足支配元素。

int minimumIndex(vector<int>& nums) 
{
   unordered_map<int,int> ump1;
   unordered_map<int,int> ump2;
   //记录每个元素以及对应次数
   for(auto it:nums)
     ump1[it]++;
   int n=nums.size();
   //遍历数组
   for(int i=0;i<n;i++)
   {
    //从i处进行划分
    ump2[nums[i]]++;
    if(ump2[nums[i]]*2>(i-0+1)&&(ump1[nums[i]]-ump2[nums[i]])*2>(n-i-1))
      return i;
   }
   return -1;
}

总结:永远不要忘记暴力算法,很多优化都是在暴力的基础上进行优化的,不可能一步登天的。统计次数就用哈希表,分割判断支配元素就一次遍历。

6924. 最长合法子字符串的长度

题目描述

描述:给你一个字符串 word 和一个字符串数组 forbidden 。

如果一个字符串不包含 forbidden 中的任何字符串,我们称这个字符串是 合法 的。

请你返回字符串 word 的一个 最长合法子字符串 的长度。

子字符串 指的是一个字符串中一段连续的字符,它可以为空。

示例 1:

输入:word = "cbaaaabc", forbidden = ["aaa","cb"]
输出:4
解释:总共有 9 个合法子字符串:"c" ,"b" ,"a" ,"ba" ,"aa" ,"bc" ,"baa" ,"aab" 和 "aabc" 。最长合法子字符串的长度为 4 。
其他子字符串都要么包含 "aaa" ,要么包含 "cb" 。

示例 2:

输入:word = "leetcode", forbidden = ["de","le","e"]
输出:4
解释:总共有 11 个合法子字符串:"l" ,"t" ,"c" ,"o" ,"d" ,"tc" ,"co" ,"od" ,"tco" ,"cod" 和 "tcod" 。最长合法子字符串的长度为 4 。
所有其他子字符串都至少包含 "de" ,"le" 和 "e" 之一。

提示:

1 <= word.length <= 105
word 只包含小写英文字母。
1 <= forbidden.length <= 105
1 <= forbidden[i].length <= 10
forbidden[i] 只包含小写英文字母。

解题思路

思路:首先要判断子字符串是否在字符串数组中,故需要将字符串数组转换为uset,由于要判断连续子字符串是否在字符串数组中,故可以使用滑动窗口同向双指针。首先left和right均初始化指向0,接着移动右指针,此时从右指针向左指针逆向判断子串是否在字符串数组中,一旦发现则停止遍历,并将左指针移到刚好不在字符串数组的那个位置,即在字符串数组中的子串起始位置的下一个位置,并统计最大长度。

int longestValidSubstring(string word, vector<string>& forbidden) 
{
  // 做题先看一下数据范围
  unordered_set<string> fb(forbidden.begin(),forbidden.end());
  int res=0;
  int left=0,right=0,n=word.size();
  for(right=0;right<n;right++)
  {
      //从右向左遍历  forbid最长为10 否则aaa...aaa超时
      for(int i=right;i>=left&&i>right-10;i--)
      {
        if(fb.count(word.substr(i,right-i+1)))
        {
            //左指针指向刚好不在哈希表中的那一个
            left=i+1;
            break;
        }
      }
      res=max(res,right-left+1);
  }
  return res;
}

总结:首先,做题先看数据范围,很多时候都是先暴力再考虑如何优化。其次,做题也不要过于惯性思维,比如一看到字符串匹配就想起字典树。

猜你喜欢

转载自blog.csdn.net/qq_43779149/article/details/131754047