加载中...
1520-最多的不重叠子字符串(Maximum Number of Non-Overlapping Substrings)
发表于:2021-12-03 | 分类: 困难
字数统计: 1.6k | 阅读时长: 7分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/maximum-number-of-non-overlapping-substrings

英文原文

Given a string s of lowercase letters, you need to find the maximum number of non-empty substrings of s that meet the following conditions:

  1. The substrings do not overlap, that is for any two substrings s[i..j] and s[k..l], either j < k or i > l is true.
  2. A substring that contains a certain character c must also contain all occurrences of c.

Find the maximum number of substrings that meet the above conditions. If there are multiple solutions with the same number of substrings, return the one with minimum total length. It can be shown that there exists a unique solution of minimum total length.

Notice that you can return the substrings in any order.

 

Example 1:

Input: s = "adefaddaccc"
Output: ["e","f","ccc"]
Explanation: The following are all the possible substrings that meet the conditions:
[
  "adefaddaccc"
  "adefadda",
  "ef",
  "e",
  "f",
  "ccc",
]
If we choose the first string, we cannot choose anything else and we'd get only 1. If we choose "adefadda", we are left with "ccc" which is the only one that doesn't overlap, thus obtaining 2 substrings. Notice also, that it's not optimal to choose "ef" since it can be split into two. Therefore, the optimal way is to choose ["e","f","ccc"] which gives us 3 substrings. No other solution of the same number of substrings exist.

Example 2:

Input: s = "abbaccd"
Output: ["d","bb","cc"]
Explanation: Notice that while the set of substrings ["d","abba","cc"] also has length 3, it's considered incorrect since it has larger total length.

 

Constraints:

  • 1 <= s.length <= 10^5
  • s contains only lowercase English letters.

中文题目

给你一个只包含小写字母的字符串 s ,你需要找到 s 中最多数目的非空子字符串,满足如下条件:

  1. 这些字符串之间互不重叠,也就是说对于任意两个子字符串 s[i..j] 和 s[k..l] ,要么 j < k 要么 i > l 。
  2. 如果一个子字符串包含字符 char ,那么 s 中所有 char 字符都应该在这个子字符串中。

请你找到满足上述条件的最多子字符串数目。如果有多个解法有相同的子字符串数目,请返回这些子字符串总长度最小的一个解。可以证明最小总长度解是唯一的。

请注意,你可以以 任意 顺序返回最优解的子字符串。

 

示例 1:

输入:s = "adefaddaccc"
输出:["e","f","ccc"]
解释:下面为所有满足第二个条件的子字符串:
[
  "adefaddaccc"
  "adefadda",
  "ef",
  "e",
  "f",
  "ccc",
]
如果我们选择第一个字符串,那么我们无法再选择其他任何字符串,所以答案为 1 。如果我们选择 "adefadda" ,剩下子字符串中我们只可以选择 "ccc" ,它是唯一不重叠的子字符串,所以答案为 2 。同时我们可以发现,选择 "ef" 不是最优的,因为它可以被拆分成 2 个子字符串。所以最优解是选择 ["e","f","ccc"] ,答案为 3 。不存在别的相同数目子字符串解。

示例 2:

输入:s = "abbaccd"
输出:["d","bb","cc"]
解释:注意到解 ["d","abba","cc"] 答案也为 3 ,但它不是最优解,因为它的总长度更长。

 

提示:

  • 1 <= s.length <= 10^5
  • s 只包含小写英文字母。

通过代码

高赞题解

思路

  1. 因为条件 2 ,如果一个子字符串包含字符 c ,那么 s 中所有 c 字符都应该在这个子字符串中

  2. 可以先把每个字母找一遍,包含这个字母的子串最少应该是从第一次出现到最后一次出现

  3. 使用 vector<vector<int>> subs(26);, 找出每个字母的代表子串的范围

    1. 使用 subs[0] 表示字母 a 的子串信息, subs[1] 表示字母 b
    2. subs[i][0] 存子串长度
    3. subs[i][1] 存范围开始
    4. subs[i][2] 存范围结束

图片.png

  1. 然后再对这些子串检查一下,将其中包含其他字母的区间也都覆盖到

图片.png

  1. 按照子串长度贪心,根据子串的长度排序
    1. 这时候已经不关心到底是哪个字母的代表子串了
    2. 所有这些子串都是满足条件 2 的子串
    3. 中间可能会有因为扩展过而变得范围一样的子串和空子串,后面也会过滤掉

图片.png

  1. 根据条件 1 ,需要子串之间不重叠

  2. 使用 vector<bool> vi(s.size(), false); 记录

  3. 将符合条件 1 的子串加入答案

答题

[]
class Solution { public: vector<string> maxNumOfSubstrings(string s) { vector<vector<int>> subs(26); for (int i = 0; i < subs.size(); i++) { subs[i].push_back(INT_MAX); char c = 'a' + i; if (s.find(c) == string::npos) continue; subs[i].push_back(s.find_first_of(c)); subs[i].push_back(s.find_last_of(c)); subs[i][0] = subs[i][2] - subs[i][1] + 1; } for (int i = 0; i < subs.size(); i++) { if (subs[i].size() == 1) continue; subs[i] = getFullSub(subs, s, subs[i][1], subs[i][2]); } sort(subs.begin(), subs.end()); vector<string> ans; vector<bool> vi(s.size(), false); for (auto sub : subs) { if (sub.size() == 1) break; bool check = true; for (int j = sub[1]; j <= sub[2] && check; j++) { check = !vi[j]; } if (!check) continue; for (int j = sub[1]; j <= sub[2]; j++) { vi[j] = true; } ans.push_back(s.substr(sub[1], sub[0])); } return ans; } vector<int> getFullSub(vector<vector<int>>& subs, string& s, int left, int right) { for (int j = left + 1; j < right; j++) { int n = s[j] - 'a'; if (left <= subs[n][1] && right >= subs[n][2]) continue; left = min(left, subs[n][1]); right = max(right, subs[n][2]); j = left; } return { right - left + 1, left, right }; } };

致谢

感谢您的观看,希望对您有帮助,欢迎热烈的交流!

如果感觉还不错就点个赞吧~

这是 我的leetcode ,帮助我收集整理题目,可以方便的 visual studio 调试,欢迎关注,star

统计信息

通过次数 提交次数 AC比率
2567 7667 33.5%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
1519-子树中标签相同的节点数(Number of Nodes in the Sub-Tree With the Same Label)
下一篇:
1539-第 k 个缺失的正整数(Kth Missing Positive Number)
本文目录
本文目录