最多的不重叠子字符串
难度:
标签:
题目描述
代码结果
运行时间: 156 ms, 内存: 50.7 MB
/*
* 题目思路:
* 1. 遍历字符串,记录每个字符第一次和最后一次出现的位置。
* 2. 使用贪心算法,依次选择每个子字符串,使得每个字符只出现在一个子字符串中。
* 3. 记录选择的子字符串,返回最终结果。
*/
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Solution {
public List<String> maxNumOfSubstrings(String s) {
int n = s.length();
int[] start = new int[26];
int[] end = new int[26];
IntStream.range(0, 26).forEach(i -> {
start[i] = n;
end[i] = -1;
});
IntStream.range(0, n).forEach(i -> {
char c = s.charAt(i);
start[c - 'a'] = Math.min(start[c - 'a'], i);
end[c - 'a'] = Math.max(end[c - 'a'], i);
});
List<String> res = new ArrayList<>();
int[] right = {-1};
IntStream.range(0, n).forEach(i -> {
if (i == start[s.charAt(i) - 'a']) {
int newRight = expand(s, i, end);
if (newRight != -1) {
res.add(s.substring(i, newRight + 1));
right[0] = newRight;
}
}
if (i == right[0]) {
right[0] = -1;
}
});
return res;
}
private int expand(String s, int i, int[] end) {
int right = end[s.charAt(i) - 'a'];
for (int j = i; j <= right; j++) {
if (end[s.charAt(j) - 'a'] > right) {
return -1;
}
}
return right;
}
}
解释
方法:
这道题的解决方案基于图的深度优先搜索。首先,记录每个字符在字符串中的所有位置,然后构建一个图,图中的每个节点代表一个字符,边表示两个字符的位置有重叠,即一个字符的位置区间包含了另一个字符。对每个字符执行深度优先搜索,以确定可以合并为一个最小区间的所有字符,这个区间就代表了一个有效的子字符串。所有这些子字符串根据长度进行排序,最后通过遍历这些区间,选择那些不重叠的区间,即构成解的一部分。算法需要确保每个字符至多只在一个选择的子字符串中出现。
时间复杂度:
O(n + 26^2 + V log V)
空间复杂度:
O(n + V)
代码细节讲解
🦆
为什么在构建图时,需要比较每个字符的位置区间是否有重叠?这种重叠的具体条件是什么?
▷🦆
在DFS遍历的过程中,为何要更新和比较字符的最左和最右位置?这种更新对解决问题有什么帮助?
▷🦆
解题思路中提到,算法最终需要选择不重叠的子字符串。请问在实现中是如何确保选择的子字符串之间不会重叠的?
▷🦆
你如何理解题目中要求的 ‘如果一个子字符串包含字符char,则s中所有char字符都应该在这个子字符串中’ 这一条件?这在你的算法实现中是如何体现的?
▷