一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
题目描述
给你一个字符串 s 和一个整数 repeatLimit ,用 s 中的字符构造一个新字符串 repeatLimitedString ,使任何字母 连续 出现的次数都不超过 repeatLimit 次。你不必使用 s 中的全部字符。
返回 字典序最大的 repeatLimitedString 。
如果在字符串 a 和 b 不同的第一个位置,字符串 a 中的字母在字母表中出现时间比字符串 b 对应的字母晚,则认为字符串 a 比字符串 b 字典序更大 。如果字符串中前 min(a.length, b.length) 个字符都相同,那么较长的字符串字典序更大。
示例 1:
输入:s = "cczazcc", repeatLimit = 3
输出:"zzcccac"
解释:使用 s 中的所有字符来构造 repeatLimitedString "zzcccac"。
字母 'a' 连续出现至多 1 次。 字母 'c' 连续出现至多 3 次。 字母 'z' 连续出现至多 2 次。 因此,没有字母连续出现超过 repeatLimit 次,字符串是一个有效的 repeatLimitedString 。 该字符串是字典序最大的 repeatLimitedString ,所以返回 "zzcccac" 。 注意,尽管 "zzcccca" 字典序更大,但字母 'c' 连续出现超过 3 次,所以它不是一个有效的 repeatLimitedString 。
简单模拟+贪心
思路很简单:通过cnt数组记录原串当中所有字母出现次数,从后往前枚举,如果出现次数不超过限制就直接加到答案里,如果长度超出限制就向前借一个字符,如果没法向前借一个就结束循环。具体实现步骤如下:
- 记录每一个字母的词频
- 优先选择字母序最大的字母,拼接min(repeatLimit, cnt[j])个
- 若当前字母i还有剩,则拼接一个字母序次大的字母j,再继续拼接当前字母i直到无法拼接
- 若当前已经没有字母i,则找下一个字母序最大的字母,重复以上操作
class Solution {
public String repeatLimitedString(String s, int repeatLimit) {
int len = s.length();
int[] map = new int[26];
for(int i = 0;i<len;i++){
char a = s.charAt(i);
map[a-'a']++;
}
StringBuilder sb = new StringBuilder();
int index = 25;
while(index>=0){
boolean f = true;
if(map[index]>0){
f = false;
int times = Math.min(map[index],repeatLimit);
for(int k = 0;k<times;k++){
sb.append((char)(index+'a'));
}
if(map[index]>repeatLimit){
int temp = index-1;
while(temp>=0){
if(map[temp]>0){
sb.append((char)(temp+'a'));
map[temp]--;
break;
}
temp--;
}
if(temp<0){
f = true;
}
}
map[index] -= times;
}
if(f){
index--;
}
}
return sb.toString();
}
}
注意点
这里用的贪心思路是:统计s字母的个数count 要使字典序最大,应优先选择最大的字母 i,选择min{count[i], repeatLimit}个。 但是如果字母i 已被选完,那么继续选择下一个较小的字母。 那么如果字母 i 未被选完,为了不让字母 i 连续出现次数超过repeatLimit,可以选择一个较小的字母放在后面,然后再继续选择字母i。
如果不能继续选择,请检查自己是否保证了,符合字典序大的字母尽可能放在前面,如果该字母可使用次数count小于repeatLimit,直接让该字母连续出现repeatLimit次数,否则在该字母连续出现repeatLimit次数后,插入一个字典序次高的字母。