P13885 [蓝桥杯 2023 省 Java/Python A] 反异或 01 串 题解
XYZyyds_3799 · · 题解
前置知识
Manacher 算法,模板 P3805。
题意
给定目标 01 串
思路
每次只能在两端添加 0 或 1,那么题目可以转化为:存在一个与目标串
核心结论
反异或后的字符串必为回文串。
:::info[证明]{close}
设串
对任意位置
故
要让
- 若
T_i = 1 ,让S_i 与S_{n-i-1} 其中一个为 0,另一个为 1,这样反异或后两者都变为 1; - 若
T_i = 0 ,直接让S_i = S_{n-i-1} = 0 即可。
找到
最终答案为
需注意反异或后回文中心必为 0,所以回文中心不能是 1。
快速统计子串中 1 的数量可用前缀和预处理优化。
代码实现
#include <iostream>
#include <vector>
using namespace std;
string s,str="#";
int cnt=0,pre[2000005];
int manacher(string s)
{
int n=s.size();
vector<int> p(n,0);
int mx=0,id=0,ans=0;
for(int i=0;i<n;i++)
{
if(i<mx) p[i]=min(p[2*id-i],mx-i);
else p[i]=1;
while(i-p[i]>=0&&i+p[i]<n&&s[i-p[i]]==s[i+p[i]]) p[i]++;
if(i+p[i]>mx)
{
mx=i+p[i];
id=i;
}
if(s[i]=='1') continue;//回文中心只能是0或#
ans=max(ans,pre[i+p[i]]-pre[i-p[i]]);//前缀数组差分计算子串内1的数量
}
return ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>s;
for(char c:s)//预处理
{
str.push_back(c);
str.push_back('#');
cnt+=(c=='1');
}
if(cnt==0)//特判:T内无1,加0即可
{
cout<<0<<endl;
return 0;
}
for(int i=0;i<str.size();i++)//预处理前缀数组
{
pre[i+1]=pre[i]+(str[i]=='1');
}
cout<<cnt-manacher(str)/2<<'\n';
return 0;
}