Did We Get Everything Covered?(我们是否把所有事情都考虑到了?)
时间限制:2s 内存限制:256MB
【原题地址】
【问题描述】
给你两个整数 n 和 k 以及一个字符串 s 。
您的任务是检查是否所有长度为 n 的字符串都可以用前 k 个小写英文字母组成,并作为 s 的子序列出现。如果答案是否定的,那么您还需要打印一个长度为 n 的字符串,该字符串可以用第一个 k 小写英文字母组成,但不会作为 s 的子序列出现。
如果有多个答案,您可以打印任意一个。
注: 如果从 b 中删除一些字符(可能为零)而不改变其余字符的顺序,就可以得到 a ,那么字符串 a 就被称为另一个字符串 b 的子串。
【输入格式】
第一行输入包含一个整数 t(1≤t≤10^5) ,即测试用例的数量。
每个测试用例的第一行包含 3 个整数 n(1≤n≤26),k(1≤k≤26),m(1≤m≤1000) ,其中 n 和 k 与输入中的描述相同, m 是字符串 s 的长度。
每个测试用例的第二行包含一个长度为 m 的字符串 s ,其中只有前 k 个小写英文字母。
保证所有测试用例的 m 和 n 之和不超过 10^6 。
【输出格式】
对于每个测试用例,如果用前 k 个小写英文字母组成的长度为 n 的所有可能字符串都出现在 s 的子序列中,则打印 “是”,否则打印 “否”。
如果答案为 “否”,请在下一行中打印一个长度为 n 的字符串,该字符串可以用前 k 个小写英文字母组成,但不会作为 s 的子序列出现。
您可以在任何情况下打印 YES 或 NO 的每个字母(例如,YES、yES、YeS 都将被视为肯定答案)。
【样例输入】
3
2 2 4
abba
2 2 3
abb
3 3 10
aabbccabab
【样例输出】
YES
NO
aa
NO
ccc
【样例说明】
对于第一个测试用例,所有可能的长度为 2 的字符串(aa、ab、ba、bb),只要能用前 2 个英文字母组成,都会作为 abba 的子序列出现。长度为 2 的所有可能字符串(aa、ab、ba、bb)。
对于第二个测试用例,字符串 aa 不是 abb 的子序列。
【解题思路】
老汉使用到的是贪心的解题方式
本题是求字符串 s 是否满足。用前 k 个小写英文字母组成的长度为 n 的所有可能字符串都为 s 的子序列。
我们只需贪婪的去求一个用前 k 个小写英文字母组成的长度为 n 的字符串 strb , strb 贪婪地做到需要 n 组完整且无序的 ‘a’ ~ ‘a’+k-1 序列,如果 s 足够凑出 strb 则答案为 YES ,否则为 NO 。
代码注释有详细过程
【代码】
package CF1925_C_DidWeGetEverythingCovered;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 接收题目数据t
int t = scan.nextInt();
// 一共t组数据,分别计算输出结果
while (t-- > 0) {
// 接收数据n
int n = scan.nextInt();
// 接收数据k
int k = scan.nextInt();
// 接收数据m
int m = scan.nextInt();
// 接收数据s
String str = scan.next();
// 创建字符串strb,用于存放长度为n,是极其难构成的,每个字符都需要一组完整且无序的a~a+k-1的序列构成。
// 当strb能被s构成,即长度等于n时,答案为YES,否则,即长度小于n时,答案为NO
StringBuffer strb = new StringBuffer();
// 布尔数组used,表示当前下标对应的字符是否被使用,默认为false(未使用)
boolean[] used = new boolean[k];
// 用于判断是否足够凑出strb的一个字符序列
int count = 0;
// 遍历str字符串中的每个字符
for (char c : str.toCharArray()) {
// 当strb能被s构成时,退出循环,输出答案YES
if (strb.length() == n) {
break;
}
// 当前字符未被使用过,对count进行加1,标记当前字符为已使用
if (!used[c - 'a']) {
count++;
used[c - 'a'] = true;
}
// 当使用过的字符数足够凑出一个strb字符时,对count、used设置回初始状态,将当前字符添加进strb
if (count == k) {
count = 0;
used = new boolean[k];
strb.append(c);
}
}
// 当strb能被s构成时,输出答案YES,否则输出答案NO以及strb
if (strb.length() == n) {
System.out.println("YES");
} else {
System.out.println("NO");
// 遍历a~a+k-1,对strb进行补齐
for (int i = 0; i < k; i++) {
// 当对应字符未被使用时,即代表最后一组凑出strb字符的序列组至str末尾都未出现过,使用该字符对strb补齐为止
if (!used[i]) {
while (strb.length() < n) {
strb.append((char) ('a' + i));
}
// 补齐后退出循环
break;
}
}
// 输出结果strb
System.out.println(strb.toString());
}
}
scan.close();
}
}