LeetCode破解之构造限制重复的字符串

89 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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次数后,插入一个字典序次高的字母。