这道题目要求我们通过两种操作将给定的二进制字符串转换为交替字符串。交替字符串的定义是相邻字符不相同,比如"0101"或"1010"这样的模式。我们需要找到最少的类型2操作(反转字符)次数来实现这个目标。
理解题目时需要注意几个关键点:
对于偶数长度的字符串,我们可以发现:
对于奇数长度的字符串,情况稍微复杂一些:
对于奇数长度字符串的解法,题解中提出了一个巧妙的思路:
这个方法的正确性基于一个关键观察:奇数长度的交替字符串,其奇数位子串和偶数位子串的首尾有特定的关系,可以通过移位来满足整体交替的要求。
cpp复制if((s.length()&1)==0) {
for(int i=0;i<s.length();i++)
if((i&1)+s[i]-'0'==1)
ans++;
ans=min(ans,(int)s.length()-ans);
}
这段代码处理偶数长度字符串:
(i&1)+s[i]-'0'==1这个条件判断当前位置是否与期望模式匹配cpp复制vector<int>begin(s.length(),0);
vector<int>end(s.length(),0);
int tmp_count=0;
for(int i=0;i<s.length();i++) {
if((i&1)+s[i]-'0'==1)
tmp_count++;
begin[i]=min(tmp_count,i+1-tmp_count);
}
这部分代码计算前缀数组:
begin[i]表示前i+1个字符转换为交替子串所需的最小反转次数begin数组中cpp复制tmp_count=0;
for(int i=s.length()-1;i>=0;i--) {
if((i&1)+s[i]-'0'==1)
tmp_count++;
end[i]=min(tmp_count,(int)s.length()-i-tmp_count);
}
这部分计算后缀数组:
end[i]表示从位置i到字符串末尾的子串转换为交替子串所需的最小反转次数cpp复制ans=min(begin[s.length()-1],end[0]);
for(int i=1;i<s.length();i++) {
ans=min(ans,begin[i-1]+end[i]);
}
最后这部分通过组合前后缀来找到全局最优解:
对于奇数长度字符串,关键在于:
这个问题虽然看起来是纯粹的算法题,但实际上有一些实际应用场景:
类似的思路也可以应用于其他字符串处理问题,特别是那些需要考虑循环移位和局部最优解的问题。
在实现这个算法时,容易犯的错误包括:
调试技巧:
让我们更详细地看看代码中的一些关键部分:
cpp复制if((i&1)+s[i]-'0'==1)
这个条件判断当前位置是否与"0101..."模式匹配:
i&1:判断当前是奇数位还是偶数位s[i]-'0':将字符转换为数字cpp复制begin[i]=min(tmp_count,i+1-tmp_count);
这里计算前i+1个字符的最小反转次数:
tmp_count是转换为一种模式的反转次数i+1-tmp_count是转换为另一种模式的反转次数cpp复制ans=min(ans,begin[i-1]+end[i]);
这部分是算法的核心,通过组合前后缀来找到最优解:
begin[i-1]是前i个字符的最优解end[i]是从第i个字符开始的后缀的最优解为了证明这个算法的正确性,我们需要说明:
对于偶数长度字符串:
对于奇数长度字符串:
为了验证算法的正确性和效率,可以进行以下测试:
小规模测试:
大规模测试:
边界测试:
在实际测试中,这个算法表现良好,能够处理最大规模的输入在合理时间内完成。