题目:
给你一个仅由字符 '0'
和 '1'
组成的字符串 s
。一步操作中,你可以将任一 '0'
变成 '1'
,或者将 '1'
变成 '0'
。
交替字符串 定义为:如果字符串中不存在相邻两个字符相等的情况,那么该字符串就是交替字符串。例如,字符串 "010"
是交替字符串,而字符串 "0100"
不是。
返回使 s
变成 交替字符串 所需的 最少 操作数。
解法一(模拟思想):
根据题意,经过多次操作,s 可能会变成两种不同的交替二进制字符串,即:
- 开头为 0,后续交替的字符串;
- 开头为 1,后续交替的字符串。
因此,笔者模拟了上述生成交替字符的修改操作,模拟开头为0后续交替的字符串并记录修改的操作以及模拟开头为1后续交替的字符串并记录修改的操作,取两者的最小值作为最终的最少操作数。如下为笔者代码:
class Solution {
public:
int minOperations(string s) {
int num1=0;
int num2=0;
int length = s.size();
//Stack1栈表示模拟开头为原始字符的操作,Stack2栈表示模拟开头为非原始字符的操作
stack<char> Stack1;
stack<char> Stack2;
//for循环遍历s字符串,将所有内容保存至Stack1栈中,并使其保持交替二进制形式
for(int i=0; i<length;i++){
if(Stack1.empty()){
Stack1.push(s[i]);
}
else if(Stack1.top()==s[i]){
if(s[i]=='0'){
Stack1.push('1');
}
else{
Stack1.push('0');
}
num1++;
}
else{
Stack1.push(s[i]);
}
}
//如果原始字符串s开头为0,则在Stack2中模拟时开头应修改为1,反之若s开头为1字符,则在Stack2栈中模拟开头应修改为0
if(s[0]=='1'){
Stack2.push('0');
}
else{
Stack2.push('1');
}
num2++;
//for循环遍历s字符串,将所有内容保存至Stack2栈中,并使其保持交替二进制形式
for(int i=1;i<length;i++){
if(Stack2.top()==s[i]){
if(s[i]=='0'){
Stack2.push('1');
}
else{
Stack2.push('0');
}
num2++;
}
else{
Stack2.push(s[i]);
}
}
//取模拟的有且仅有两种情况的修改操作,取Stack1和Stack2两者之间较小的修改操作数,即为最终的最少操作数值
int result = min(num1,num2);
return result;
}
};
解法二(优化解法一):
注意到,上述模拟Stack1和Stack2两种情况下,变成这两种不同的交替二进制字符串所需要的最少操作数加起来等于 s 的长度(0和1互补),我们只需要计算出变为其中一种字符串的最少操作数,就可以推出另一个最少操作数,然后取最小值即可。如下为根据上述加粗内容性质,实现代码如下所示:
class Solution {
public:
int minOperations(string s) {
int cnt = 0;
for (int i = 0; i < s.size(); i++) {
char c = s[i];
//('0' + i % 2)的含义为零一交替进行的字符串
if (c != ('0' + i % 2)) {
cnt++;
}
}
//直接根据性质,取s.size()的补数即为另一个模拟情况所需要的操作数
return min(cnt, (int)s.size() - cnt);
}
};
-
时间复杂度:O(n),其中 n 为输入 s 的长度,仅需遍历一遍字符串。
-
空间复杂度:O(1),只需要常数额外空间。
笔者小记:
除了使用恰当合适的数据结构以简化代码编写,更快速便捷的实现问题解决之外,还需要考虑问题分类时所产生的一些隐藏“性质”和“关系”,这些相比使用更“高级”的数据结构,可以大大降低解决问题的复杂度【时间复杂度(for循环的使用)和空间复杂度(数据结构所存储的数据)】,实现代码“质”的优化。例如本题中所使用的if (c != ('0'+ i % 2))条件判断以及min(cnt, (int)s.size - cnt)模拟过程产生的性质的直接取补数并判断最小的操作。