小C的字符串
问题描述
小M拥有一个由字母 'a' 和 'b' 组成的字符串。她可以进行以下两种操作:
- 找到下标 ii,满足 ai=′b′ai=′b′ 且 ai+1=′a′ai+1=′a′,并交换这两个字符。
- 找到下标 ii,满足 ai=′a′ai=′a′ 且 ai+1=′b′ai+1=′b′,并删除这两个字符。
其中,操作 2 可以进行无限次,而操作 1 只能执行 kk 次。请帮助小M通过这两种操作,尽量减少字符串的长度,并输出最终得到的最小字符串。如果所有字符都可以被删除,输出 -1。
例如:给定字符串 "ababbaa" 和 k=1k=1,通过一次交换和多次删除后,最终得到的字符串为 "a"。
测试样例
样例1:
输入:
n = 7 ,k = 1 ,a = "ababbaa"
输出:'a'
样例2:
输入:
n = 5 ,k = 0 ,a = "aabaa"
输出:'aaa'
样例3:
输入:
n = 6 ,k = 2 ,a = "bbabba"
输出:'bb'
数据结构选择
- 使用
StringBuffer来处理字符串的修改操作,因为它支持高效的插入和删除操作。
思路分析
-
优先删除操作:
- 由于操作2可以无限次执行,因此我们应该优先考虑删除
'a'和'b'相邻的组合。这样可以最大限度地减少字符串的长度。
- 由于操作2可以无限次执行,因此我们应该优先考虑删除
-
交换操作:
- 如果操作2无法继续执行(即没有
'a'和'b'相邻的组合),我们可以考虑使用操作1来创造新的'a'和'b'相邻的组合,以便继续执行删除操作。 - 注意,操作1只能执行
k次,因此我们需要谨慎使用。
- 如果操作2无法继续执行(即没有
-
循环处理:
- 我们可以使用一个循环来不断执行删除操作,直到没有
'a'和'b'相邻的组合为止。 - 在每次循环中,检查是否可以执行交换操作来创造新的删除机会。
- 我们可以使用一个循环来不断执行删除操作,直到没有
-
边界情况:
- 如果字符串长度为0,说明所有字符都被删除了,返回
-1。 - 如果字符串长度不为0,返回最终的字符串。
- 如果字符串长度为0,说明所有字符都被删除了,返回
具体思路
-
初始字符串处理:
- 创建一个
StringBuffer对象sBuffer,将输入字符串a复制到其中,以便进行后续的字符删除操作。 - 使用一个循环遍历
sBuffer,条件是当前索引i小于sBuffer的长度减 1。在这个循环中,如果发现当前字符为'a'且下一个字符为'b',就删除这两个字符(通过sBuffer.delete(i, i + 2)),然后将索引i减 1,以确保在删除字符后能够正确地继续检查新的字符组合。如果i变为 -1,则将其重置为 0。如果i为 0 且sBuffer的长度小于等于 1,则跳出循环。如果没有发现'a'和'b'的组合,则将索引i加 1。
- 创建一个
-
重复操作:
- 接下来进行
k次循环操作。在每次循环中,设置一个标志变量flag初始值为 1。然后再次遍历sBuffer,条件是当前索引j小于sBuffer的长度减 1。在这个循环中,如果发现当前字符为'b'且下一个字符为'a',就删除这两个字符,并像之前一样处理索引j。如果在这个过程中j变为 -1,则将其重置为 0。如果j为 0 且sBuffer的长度小于等于 1,则将flag设为 0 并跳出循环。如果没有发现'b'和'a'的组合,则将索引j加 1。如果flag为 0,表示在这次循环中没有成功完成操作,就跳出外层循环。
- 接下来进行
-
最终处理和返回结果:
- 最后,再次进行与初始字符串处理类似的操作,检查并删除
'a'和'b'的组合。 - 如果
sBuffer的长度为 0,说明经过一系列操作后字符串被完全删除,此时返回“-1”;否则,返回sBuffer转换为字符串后的结果。
- 最后,再次进行与初始字符串处理类似的操作,检查并删除
代码实现
public class Main {
public static String solution(int n, int k, String a) {
// write code here
StringBuffer sBuffer = new StringBuffer(a);
for(int i = 0; i < sBuffer.length() - 1; ){
if(sBuffer.charAt(i) == 'a' && sBuffer.charAt(i + 1) == 'b'){
sBuffer.delete(i, i + 2);
i--;
if(i == -1){
i = 0;
}
if(i == 0 && sBuffer.length() <= 1){
break;
}
}else{
i++;
}
}
for(int i = 0; i < k; i++){
int flag = 1;
for(int j = 0; j < sBuffer.length() - 1; ){
if(sBuffer.charAt(j) == 'b' && sBuffer.charAt(j + 1) == 'a'){
sBuffer.delete(j, j + 2);
j--;
if(j == -1){
j = 0;
}
if(j == 0 && sBuffer.length() <= 1){
flag = 0;
break;
}
}else{
j++;
}
}
if(flag == 0){
break;
}
}
for(int i = 0; i < sBuffer.length() - 1; ){
if(sBuffer.charAt(i) == 'a' && sBuffer.charAt(i + 1) == 'b'){
sBuffer.delete(i, i + 2);
i--;
if(i == -1){
i = 0;
}
if(i == 0 && sBuffer.length() <= 1){
break;
}
}else{
i++;
}
}
if(sBuffer.length() == 0){
return "-1";
}
return sBuffer.toString(); // placeholder return
}
public static void main(String[] args) {
System.out.println(solution(7, 1, "ababbaa").equals("a"));
System.out.println(solution(5, 0, "aabaa").equals("aaa"));
System.out.println(solution(6, 2, "bbabba").equals("bb"));
}
}
关于字符串处理
一、字符串处理技巧
- 使用
StringBuffer或StringBuilder:当需要对字符串进行频繁的修改操作时,如这段代码中的多次删除操作,使用StringBuffer或StringBuilder比使用不可变的String类更高效。StringBuffer或StringBuilder提供一系列方法来方便地进行字符串的修改,而不会产生大量的中间字符串对象。 - 遍历和索引控制:在遍历字符串时,要注意正确控制索引。这段代码中通过仔细地处理索引的增减,确保在删除字符后能够正确地继续遍历字符串,避免遗漏或重复处理字符。特别是在删除操作后,需要根据具体情况调整索引,以保证后续的遍历能够覆盖所有的字符。
二、循环和条件判断的运用
- 多重循环的结构:代码中使用多重循环来重复执行特定的操作。在设计多重循环时,要确保每个循环的目的清晰,并且循环条件合理。同时,要注意循环之间的关系,避免不必要的重复或遗漏。
- 条件判断的准确性:通过精确的条件判断来确定何时执行特定的操作。例如,在这段代码中,根据字符的组合情况进行判断,决定是否删除字符。条件判断应该尽可能准确地反映问题的逻辑,以确保程序的正确性。
三、错误处理和边界情况考虑
- 边界情况:代码中考虑一些边界情况,如字符串长度为 0 或 1 时的处理,以及索引变为 -1 时的重置。在编程中,要充分考虑各种边界情况,以确保程序在各种情况下都能正确运行。
- 错误处理:当出现特定情况(如字符串被完全删除)时,代码返回特定的标识“-1”。这是一种简单的错误处理方式,可以让调用者知道出现了异常情况。在实际编程中,应该根据具体需求设计更详细的错误处理机制,以便更好地处理各种错误情况。