leetcode题解767-重构字符串

76 篇文章 1 订阅

问题描述

给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。
若可行,输出任意可行的结果。若不可行,返回空字符串。

示例 1:
输入: S = “aab”
输出: “aba”
示例 2:
输入: S = “aaab”
输出: “” 注意:

S 只包含小写字母并且长度在[1, 500]区间内。

解题思路

重构字符串时,需要根据每个字母在字符串中出现的次数处理每个字母放置的位置。如果出现次数最多的字母可以在重新排布之后不相邻,则可以重新排布字母使得相邻的字母都不相同。如果出现次数最多的字母过多,则无法重新排布字母使得相邻的字母都不相同。

假设字符串的长度为 n,如果可以重新排布成相邻的字母都不相同的字符串,每个字母最多出现多少次?

当 n 是偶数时,有 n/2 个偶数下标和 n/2 个奇数下标,因此每个字母的出现次数都不能超过 n/2 次,否则出现次数最多的字母一定会出现相邻。

当 n是奇数时,由于共有 (n+1)/2 个偶数下标,因此每个字母的出现次数都不能超过 (n+1)/2 次,否则出现次数最多的字母一定会出现相邻。

由于当 n是偶数时,在整数除法下满足 n/2 和 (n+1)/2 相等,因此可以合并 n 是偶数与 n 是奇数的情况:如果可以重新排布成相邻的字母都不相同的字符串,每个字母最多出现 (n+1)/2 次。

基于此,我们可以使用基于最大堆的贪心算法
维护最大堆存储字母,堆顶元素为出现次数最多的字母。首先统计每个字母的出现次数,然后将出现次数大于 0 的字母加入最大堆。

当最大堆的元素个数大于 1 时,每次从最大堆取出两个字母,拼接到重构的字符串,然后将两个字母的出现次数分别减 1,并将剩余出现次数大于 0 的字母重新加入最大堆。由于最大堆中的元素都是不同的,因此取出的两个字母一定也是不同的,将两个不同的字母拼接到重构的字符串,可以确保相邻的字母都不相同。

如果最大堆变成空,则已经完成字符串的重构。如果最大堆剩下 1 个元素,则取出最后一个字母,拼接到重构的字符串。

而最大堆的实现的数据结构是PriorityQueue.实现这个数据结构我们需要写一个比较器Comparator,比较器的具体实现如代码所述

代码实现

class Solution {
    public String reorganizeString(String S) {
        final int alpha[]=new int[26];
		int len=S.length();
		for(int i=0;i<len;i++){
			char ch=S.charAt(i);
			alpha[ch-'a']++;
		}
		//判断有没有可能有的字符出现超过(n+1)/2的次数
		int maxOccur=alpha[0];
		for(int i=0;i<alpha.length;i++){
			if(alpha[i]>maxOccur){
				maxOccur=alpha[i];
			}
		}
		if(maxOccur>((len+1)/2)){
			return "";
		}
        //使用优先级队列,队列的比较器为按照alpha的值从大到小进行排列,这里面参数没有数组,但其实用到了数组
		PriorityQueue<Character> queue=new PriorityQueue<>(new Comparator<Character>(){
			@Override
			public int compare(Character o1, Character o2) {
				return alpha[o2-'a']-alpha[o1-'a'];
			}
		});
        //将元素一个一个的加入进去
		for(int i=0;i<26;i++){
			if(alpha[i]>0){
				queue.offer((char) ('a'+i));
			}
		}
        //使用StringBuilder,拼接效率更高
		StringBuilder result=new StringBuilder();
        /*这里为什么size()大于1,因为下面我们每次取出两个元素,如果size()大于0,当为奇数的时候,最终size等于1,无法取出两个元素*/
		while(queue.size()>1){
            /*
            每次取出队列中两个出现次数最多的两个字符,然后添加到结果字符串中,并且更新alpha数组的值
            最后如果这两个字符对应的alpha数组的值仍然大于1,就需要重新添加到队列中进行比较
            */
			char ch1=queue.poll();
			char ch2=queue.poll();
			result.append(ch1);
			result.append(ch2);
			alpha[ch1-'a']--;
			alpha[ch2-'a']--;
			if(alpha[ch1-'a']>0){
				queue.offer(ch1);
			}
			if(alpha[ch2-'a']>0){
				queue.offer(ch2);
			}
		}
        //可能while循环结束之后,因为奇数的缘故,最后队列中可能还有元素
		if(queue.size()>0){
			result.append(queue.poll());
		}
		return result.toString();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值