思路: 如果有一个字符c的数量n(>=2),我们每次选两个该字符c,消除后再添加该字符c,最终需要(n-1)次;如果选种两个该字符c,消除后添加另一个数量为k(k>0)的字符c1,最终消除到这两个字符c、c1的数量都是1,即仅限于他们俩不重复时需要(k-1+n-1),如果k是0的话要就只需要n-2次。以此类推,我们发现答案最终取决于数量为0的字符的个数。他可以抵消一次消除。然后其他情况就都是(n-1)
public class 最小字符串操作次数 {
public static int solution(String S) {
int[] bucket=new int[26];
for (int i = 0; i < S.length(); i++) {
bucket[(int)(S.charAt(i)-97)]++;
}
//经过推理我们得出,一个数量大于2的n个字母,在自己执行时需要n-1次
// 即使将他消除后转移到其他字母,除非这个字母的数量是0
// 就像此时有一个a,其他字母消除后生成这个字母,然后就有两个a,也需要进行消除然后生成另一个字母,和原来的效果等价
//1、我们可以先统计没出现字母的次数
int zero=0;
for (int i = 0; i < bucket.length; i++) {
if(bucket[i]==0){
zero++;
}
}
//得到的这和zero就是真正消除字母后不用再执行的量
int sum=0;
for (int i = 0; i < bucket.length; i++) {
while (bucket[i]>2&&zero>0){
bucket[i]-=2;
zero--;
sum++;
}
if(bucket[i]!=0){
sum+=bucket[i]-1;
}
}
return sum;
}
public static void main(String[] args) {
//System.out.println((int)('a'));
System.out.println(solution("abab") == 2);
System.out.println(solution("aaaa") == 2);
System.out.println(solution("abcabc") == 3);
}
}
代码解析:
- 首先,我们定义了一个长度为26的整型数组
bucket,用于统计字符串中每个字母出现的次数。数组的索引对应字母在字母表中的位置(例如,bucket[0]对应字母’a’,bucket[1]对应字母’b’,以此类推)。 - 通过遍历字符串S,我们将每个字符的出现次数记录在
bucket数组中。 - 接着,我们统计数组中为0的元素个数,即未出现的字母数量,存储在变量
zero中。 - 然后,我们遍历
bucket数组,对于每个出现次数大于2的字母,我们尝试将其出现次数减少2(即删除两个相同的字母),同时zero减1(表示我们利用了一个未出现的字母位置),并将操作次数sum加1。 - 如果
bucket[i]的值不为0,说明还需要额外删除bucket[i]-1个字母,因此将sum加上bucket[i]-1。 - 最后,返回操作次数
sum。
总结:
本题的核心在于如何通过最少的操作使得字符串中的所有字母都不相同。我们可以将问题分解为以下几个步骤:
- 统计字符串中每个字母的出现次数。
- 计算未出现的字母数量,这些位置可以用来放置被删除的字母。
- 对于出现次数大于2的字母,优先将其配对删除,并利用未出现的字母位置。
- 对于剩余的出现次数为1或2的字母,直接删除多余的部分。
通过以上步骤,我们可以得到最少操作次数。