leetCode767:重构字符串

目录

一、题目描述

二、解题思路

三、代码实现


一、题目描述

给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。

若可行,输出任意可行的结果。若不可行,返回空字符串。

示例 1:

输入: S = "aab"
输出: "aba"

示例 2:

输入: S = "aaab"
输出: ""

注意:

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

二、解题思路

可以先直观经验考虑这道题,应该能想到插空法,就是在两个相同的字符之间插入一个不同的字符。那就会想到统计每种字符的出现次数,然后给他们安排位置,基本上这道题的大概思路就有了。

贪心插空,注意需要按照字符出现的次数考虑放在奇数还是偶数下标上。

  • 如果字母的出现次数大于 0且小于或等于 n/2,且 oddIndex 没有超出数组下标范围,则将字母放置在 oddIndex,然后将 oddIndex 的值加 2。
  • 如果字母的出现次数大于 n/2,或 oddIndex 超出数组下标范围,则将字母放置在 evenIndex,然后将 evenIndex 的值加 2。

可以使用反证法,证明当一个字符先出现在奇数位上然后在出现在偶数位上时,不会出现相邻的情况。

由于先出现在奇数位上,那么个数小于或等于 n/2,一旦相邻则长度至少为(n+1)/2,相互矛盾。

 

另外在题解区看见一位大佬的另一种更为直接的思路:按照字符出现的次数先从小到大排序,排序之后从次数少的字符开始直接往奇数位置上放,奇数位置放满了之后再往偶数位上放,这样可以不用在循环里做额外的判断就能保证次数等于 (n+1)/2的字符会出现在偶数位上。

三、代码实现

#include<bits/stdc++.h>  
using namespace std;
string reorganizeString(string S) {
	int n = S.size();
	if (n < 2) return S;
	vector<int> chvec(26, 0);
	int maxCnt = 0;
	for (int i = 0; i < n; ++i) {
		chvec[S[i] - 'a']++;
		maxCnt = max(maxCnt, chvec[S[i] - 'a']);
	}
	if (maxCnt > (n + 1) / 2) return "";
	string res(n, ' ');
	int evenIndex = 0, oddIndex = 1;
	int halfNums = n / 2;
	//对字符进行重排
	for (int i = 0; i < 26; ++i) {
		//出现次数小于长度一半并且奇数下标未超出范围,就放在奇数下标这种
		//这里要先写奇数下标的循环判断,先写偶数有问题
		while (chvec[i] > 0 && chvec[i] <= halfNums && oddIndex < n) {
			res[oddIndex] = 'a' + i;
			oddIndex += 2;
			chvec[i]--;
		}
		//如果长度等于一半或者一半加一,或者奇数下标已经用完了,就用偶数下标
		while (chvec[i] > 0) {
			res[evenIndex] = 'a' + i;
			evenIndex += 2;
			chvec[i]--;
		}
	}
	return res;
}
//这个思路好操作一些:按照出现次数从小到大排序之后,只要依次往奇数下标上放就行,奇数用完了再用偶数下标
//由于统计字符次数的数组会被排序打乱,所以要把字符和出现次数信息都记录在数组中
//这个数组很妙啊
string reorganizeString1(string S) {
	int n = S.size();
	vector<int> count(26, 0);
	for (auto c : S) {
		count[c - 'a'] += 100; // 统计次数
		if (count[c - 'a'] / 100 > (n + 1) / 2) return ""; // 超过(n+1)/2个字符,不可能得到答案
	}
	for (int i = 0; i < 26; ++i) count[i] += i; // 将字符信息加入数组值中
	// 此时count中的元素存储了次数和字符信息
	// 如 count[i] = 203 表示字符 'd'='a'+203%100 出现了 2=203/100 次
	sort(count.begin(), count.end()); // 对次数从小到大排序
	int index = 1;
	string ret(n, ' ');
	for (auto c : count) {
		int cnt = c / 100; // 取出次数
		char ch = 'a' + (c % 100); // 取出字符信息
		for (int i = 0; i < cnt; ++i) {
			if (index >= n) index = 0; // 第一遍走完 从0开始
			ret[index] = ch;
			index += 2; // 间隔添加相同字符
		}
	}
	return ret;
}

int main() {
	string s = "ababababbaaabbcccdef";
	cout << reorganizeString(s);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值