2182 构造限制重复的字符串(贪心 + 哈希表)

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)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值