重复 K 次的最长子序列
难度:
标签:
题目描述
给你一个长度为 n
的字符串 s
,和一个整数 k
。请你找出字符串 s
中 重复 k
次的 最长子序列 。
子序列 是由其他字符串删除某些(或不删除)字符派生而来的一个字符串。
如果 seq * k
是 s
的一个子序列,其中 seq * k
表示一个由 seq
串联 k
次构造的字符串,那么就称 seq
是字符串 s
中一个 重复 k
次 的子序列。
- 举个例子,
"bba"
是字符串"bababcba"
中的一个重复2
次的子序列,因为字符串"bbabba"
是由"bba"
串联2
次构造的,而"bbabba"
是字符串"bababcba"
的一个子序列。
返回字符串 s
中 重复 k 次的最长子序列 。如果存在多个满足的子序列,则返回 字典序最大 的那个。如果不存在这样的子序列,返回一个 空 字符串。
示例 1:
输入:s = "letsleetcode", k = 2 输出:"let" 解释:存在两个最长子序列重复 2 次:let" 和 "ete" 。 "let" 是其中字典序最大的一个。
示例 2:
输入:s = "bb", k = 2 输出:"b" 解释:重复 2 次的最长子序列是 "b" 。
示例 3:
输入:s = "ab", k = 2 输出:"" 解释:不存在重复 2 次的最长子序列。返回空字符串。
提示:
n == s.length
2 <= k <= 2000
2 <= n < k * 8
s
由小写英文字母组成
代码结果
运行时间: 1392 ms, 内存: 18.2 MB
/*
* 思路:
* 1. 使用回溯法生成所有可能的子序列,生成器函数使用流式编程。
* 2. 对每一个子序列,检查其重复k次后是否仍为原字符串的子序列。
* 3. 保存最长的符合条件的子序列,并在出现更长或者字典序更大的子序列时更新结果。
*/
import java.util.*;
import java.util.stream.*;
public class LongestRepeatingSubsequenceStream {
public String longestSubsequenceRepeatedK(String s, int k) {
String result = "";
for (int len = 1; len <= s.length() / k; len++) {
Set<String> subsequences = generateSubsequences(s, len).collect(Collectors.toSet());
for (String subseq : subsequences) {
if (isKTimesRepeatedSubsequence(s, subseq, k)) {
if (subseq.length() > result.length() || (subseq.length() == result.length() && subseq.compareTo(result) > 0)) {
result = subseq;
}
}
}
}
return result;
}
private Stream<String> generateSubsequences(String s, int len) {
List<String> result = new ArrayList<>();
generate(s, 0, len, new StringBuilder(), result);
return result.stream();
}
private void generate(String s, int index, int len, StringBuilder current, List<String> result) {
if (current.length() == len) {
result.add(current.toString());
return;
}
if (index >= s.length()) return;
generate(s, index + 1, len, current, result);
current.append(s.charAt(index));
generate(s, index + 1, len, current, result);
current.deleteCharAt(current.length() - 1);
}
private boolean isKTimesRepeatedSubsequence(String s, String subseq, int k) {
int count = 0, j = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == subseq.charAt(j)) {
j++;
if (j == subseq.length()) {
j = 0;
count++;
if (count == k) return true;
}
}
}
return false;
}
public static void main(String[] args) {
LongestRepeatingSubsequenceStream lrs = new LongestRepeatingSubsequenceStream();
System.out.println(lrs.longestSubsequenceRepeatedK("letsleetcode", 2)); // 输出: "let"
System.out.println(lrs.longestSubsequenceRepeatedK("bb", 2)); // 输出: "b"
System.out.println(lrs.longestSubsequenceRepeatedK("ab", 2)); // 输出: ""
}
}
解释
方法:
此题解的思路首先是将字符串s转换为字符的整数表示,便于后续操作。然后构建一个next数组,用于快速定位字符在字符串中的下一个出现位置,这是通过逆序遍历字符串s并记录每个字符最后出现位置来实现的。接着,利用字符频数统计,确定哪些字符的出现次数至少是k的倍数,这些字符可能构成最终答案的一部分。题解尝试所有可能的组合(长度从1到7),使用排列尝试构建子序列,检查是否能在字符串s中重复出现k次。这一步使用next数组进行快速验证。最后,返回字典序最大的有效子序列。
时间复杂度:
O(n + 7^7 * km)
空间复杂度:
O(n + 7^7)
代码细节讲解
🦆
为什么在构建next数组时选择逆序遍历字符串s?这种做法与正序遍历有何不同?
▷🦆
在题解中,字符的整数表示从0至25是如何帮助优化算法的?是否有其他方法可以达到相同的效果?
▷🦆
题解中提到使用next数组进行快速验证子序列是否可重复出现k次,具体是如何实现的?能否详细解释其逻辑和步骤?
▷🦆
为什么题解尝试的子序列长度限制在7以内?这是否意味着超过7个字符的子序列是不可能存在的?
▷