这道题目要求我们通过两种操作将给定的二进制字符串转换为交替字符串,并找出所需的最少反转次数。我们先明确几个关键概念:
交替字符串的定义是任意相邻字符都不相同,比如"0101"或"1010"。题目允许的两种操作:
通过分析题目,我们可以得出几个重要结论:
操作顺序优化:类型2操作可以全部先执行,最后再执行类型1操作。因为类型1操作只是改变字符顺序,不影响字符值。
偶数长度情况:当字符串长度为偶数时,交替字符串只有两种可能形式:"0101..."或"1010..."。我们只需要计算将原字符串转换为这两种形式所需的最小反转次数。
奇数长度情况:当字符串长度为奇数时,情况稍复杂。交替字符串可能是"01010..."或"10101..."形式。此时可能需要一次类型1操作来拼接两个交替子串。
我们使用两个预处理数组来高效计算反转次数:
pre[i][j]:表示将前i个字符转换为以j结尾的交替字符串所需的最小反转次数suf[i][j]:表示将第i个字符到末尾转换为以j开头的交替字符串所需的最小反转次数其中j可以是0或1。
对于前缀数组pre:
code复制pre[i][0] = pre[i-1][1] + (s[i] != '0')
pre[i][1] = pre[i-1][0] + (s[i] != '1')
对于后缀数组suf:
code复制suf[i][0] = suf[i+1][1] + (s[i] != '0')
suf[i][1] = suf[i+1][0] + (s[i] != '1')
pre[0][0] = (s[0] != '0')pre[0][1] = (s[0] != '1')suf[n-1][0] = (s[n-1] != '0')suf[n-1][1] = (s[n-1] != '1')cpp复制class Solution {
public:
int minFlips(string s) {
int len = s.length(), ret = INT_MAX;
int pre[len+5][2], suf[len+5][2];
// 初始化数组
for(int i=0; i<=len; ++i) {
pre[i][0] = pre[i][1] = suf[i][0] = suf[i][1] = 0;
}
// 边界条件处理
pre[0][0] = (s[0] != '0');
pre[0][1] = (s[0] != '1');
suf[len-1][0] = (s[len-1] != '0');
suf[len-1][1] = (s[len-1] != '1');
// 填充前缀数组
for(int i=1; i<len; ++i) {
pre[i][0] = pre[i-1][1] + (s[i] != '0');
pre[i][1] = pre[i-1][0] + (s[i] != '1');
// 填充后缀数组
if(len-1-i >= 0) {
suf[len-1-i][0] = suf[len-i][1] + (s[len-1-i] != '0');
suf[len-1-i][1] = suf[len-i][0] + (s[len-1-i] != '1');
}
}
// 计算结果
if(len % 2) { // 奇数长度情况
for(int i=0; i<len; ++i) {
ret = min(ret, min(pre[i][0] + suf[i+1][0], pre[i][1] + suf[i+1][1]));
}
} else { // 偶数长度情况
ret = min(pre[len-1][0], pre[len-1][1]);
}
return ret;
}
};
数组初始化:我们使用len+5来避免数组越界问题,这是一种常见的防御性编程技巧。
边界条件处理:第一个字符和最后一个字符的预处理需要单独处理。
递推填充数组:前缀数组从左到右填充,后缀数组从右到左填充。
结果计算:
空间优化:可以只保留前一个状态,而不是整个数组,将空间复杂度降为O(1)。
提前终止:在某些情况下可以提前终止计算,比如已经找到0反转的情况。
数组越界:在填充后缀数组时,容易忽略边界条件导致越界。
初始条件错误:第一个字符和最后一个字符的预处理容易出错。
奇偶情况混淆:容易忘记区分字符串长度的奇偶性。
打印中间结果:在填充前缀和后缀数组时,打印中间结果验证正确性。
小规模测试:先用小规模测试用例验证代码逻辑。
边界测试:测试长度为1的字符串和全0/全1的字符串。
大数处理:当n很大时(题目中n可达10^5),要确保算法是线性的。
内存使用:在嵌入式系统等内存受限环境中,需要考虑空间优化版本。
代码可读性:在竞赛中,清晰的代码结构比微小的性能优化更重要。
最小交换次数:如果操作改为交换相邻字符,如何求解?
多字符情况:如果字符串包含更多字符(不只是0和1),如何解决?
环形字符串:如果字符串是环形的(即首尾相连),如何解决?
滑动窗口:可以尝试用滑动窗口的方法解决这个问题。
贪心算法:在某些限制条件下,可能有贪心解法。
状态压缩:对于较小规模的n,可以考虑状态压缩动态规划。
数据编码:在通信系统中,交替字符串可以减少连续相同符号带来的问题。
测试模式生成:在芯片测试中,交替模式是常用的测试模式。
图像处理:在二值图像处理中,类似的模式转换也有应用。