1. 问题描述:
给你一个字符串 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 。
示例 2:
输入:s = "aababab", repeatLimit = 2
输出:"bbabaa"
解释:
使用 s 中的一些字符来构造 repeatLimitedString "bbabaa"。
字母 'a' 连续出现至多 2 次。
字母 'b' 连续出现至多 2 次。
因此,没有字母连续出现超过 repeatLimit 次,字符串是一个有效的 repeatLimitedString 。
该字符串是字典序最大的 repeatLimitedString ,所以返回 "bbabaa" 。
注意,尽管 "bbabaaa" 字典序更大,但字母 'a' 连续出现超过 2 次,所以它不是一个有效的 repeatLimitedString 。
提示:
1 <= repeatLimit <= s.length <= 10 ^ 5
s 由小写英文字母组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/construct-string-with-repeat-limit/
2. 思路分析:
分析题目可以知道在不超过repeatLimitedString的限制下尽可能使用字典序较大的字符这样最终构造的字符串的字典序一定是最大的,一开始想到的是逐位进行构造,使用一个count变量来记录当前构造的字符串中连续字符的数目,如果当前连续字符的次数小于repeatLimitedString那么找到当前一个字典序最大的字符拼接在结果字符串的后面,如果当前连续字符的数目等于repeatLimitedString说明我们需要使用字典序次大的字符拼接在结果集的后面,使用循环找到当前字典序次大的字符拼接在结果集的后面,这种做法的涉及到的细节比较多;后面看了一下题解,发现可以使用两个变量l和r分别维护当前次大字典序和最大字典序的位置,先求解出当前字典序最大的字符的出现次数和repeatLimitedString的最小值rep,当前需要重复rep个字典序最大的字符,如果发现字典序最大的字符使用完了那么当前字典序最大的字符的位置变为字典序次大的位置,如果发现当前字典序最大的字符还有说明需要添加一个字典序次大的字符,如果不能够添加字典序次大的字符说明构造过程结束。
3. 代码如下:
python:
class Solution:
def repeatLimitedString(self, s: str, repeatLimit: int) -> str:
mp = [0] * 30
for c in s:
k = ord(c) - ord("a")
mp[k] += 1
k = -1
for i in range(26):
if mp[i] > 0: k = i
res = ""
count = 0
while True:
i = k
flag = 0
for j in range(i, -1, -1):
if mp[j] > 0 and count < repeatLimit:
c = chr(j + 97)
if res and res[-1] == c:
count += 1
else:
count = 1
res += c
mp[j] -= 1
flag = 1
break
elif mp[j] > 0 and count == repeatLimit:
c = chr(j + 97)
if c == res[-1]: i -= 1
for j in range(i, -1, -1):
if mp[j] > 0:
c = chr(j + 97)
res += c
mp[j] -= 1
count = 1
flag = 1
break
break
else:
i -= 1
if flag == 0: break
return res
go:
package main
import (
"fmt"
)
// 根据flag判断求解两个int整数的最小值还是最大值
func get(a, b, flag int) int {
if flag == 1 {
// 求解最大值
if a >= b {
return a
}
return b
} else {
// 求解最小值
if a < b {
return a
}
return b
}
}
func repeatLimitedString(s string, repeatLimit int) string {
// 声明一个长度为26的数组
mp := [26]int{}
// r记录一开始字典序最大的字符
r := 0
for _, c := range s {
// 统计每一个字符的出现次数, k属于int32的整数, 不能与int的整数直接比较
k := c - 'a'
mp[k] += 1
// 因为两个不同类型的整数不能够直接比较, k: int32, r: int, 可以使用int()函数将其转为int类型才可以直接比较
r = get(r, int(k), 1)
}
l := r - 1
// 字符串底层是byte
res := []byte{}
// 当r大于0的时候执行循环
for r >= 0 {
// 找到次大的元素位置
for l >= 0 && mp[l] == 0 {
l -= 1
}
rep := get(mp[r], repeatLimit, 0)
// 重复rep次的字典序最大的字符
for i := 0; i < rep; i++ {
mp[r] -= 1
res = append(res, 'a'+byte(r))
}
// 字典序最大的字符已经用完了, 次大的字符变为最大的字符
if mp[r] == 0 {
r = l
l = r - 1
} else if l < 0 {
break
} else {
// 'a'属于int32类型的数字, byte属于uint8类型的数字, 相加结果为unit8类型的整数
res = append(res, 'a'+byte(l))
mp[l] -= 1
}
}
return string(res)
}