题目大意
给定两个长度为 n n n 的 01 串 s , t s,t s,t,每个串都不会有连续三个相同的字符。现在每次操作可以将 s s s 的一位反转,但反转之后也要保证没有连续三个相同的字符,求最少的步数使得 s s s 变成 t t t。
n
≤
5000
n \le 5000
n≤5000
2s
\\
\\
\\
题解
题解这个模型转化非常 nice。
给 0 0 0 和 1 1 1 之间画一条红线,给 1 1 1 和 0 0 0 之间画一条蓝线,这样这个 01 串就变成了:
- 有很多红蓝相间的线;(开头和结尾可以补充无限条红蓝相间的线)
- 相邻的线间隔为 1 或 2;
- 每次操作可以将一条线往左或者往右移 1 位,不能与别的线重合,线的间隔仍然要为 1 或 2。
可以发现这样就变成了线的匹配问题。 O ( n ) O(n) O(n) 枚举 s s s 串开头的线匹配 t t t 的哪一条线,然后再 O ( n ) O(n) O(n) 算出匹配代价即可,总共 O ( n 2 ) O(n^2) O(n2)。整个过程用些数据结构或者 trick 可以优化成 O ( n ) O(n) O(n),不过意义不大。
代码
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef pair<int,int> pr;
const int maxn=5005;
int n,ps0,ps1,pt0,pt1,ps2,pt2;
pr ps[4*maxn],pt[4*maxn];
char s[maxn],t[maxn];
void pre(char *t,pr *p,int &p0,int &p1,int &p2) {
fo(i,0,n+5) p[++p0]=make_pair(0,i&1);
if (p[p0].second!=(t[0]>t[1])) p[++p0]=make_pair(0,(t[0]>t[1]));
p1=p0+1;
fo(i,1,n-1) if (t[i]!=t[i+1]) p[++p0]=make_pair(i,(t[i]>t[i+1]));
p2=p0;
int cur=(t[n]=='1');
fo(i,0,n+5) p[++p0]=make_pair(n,cur), cur^=1;
}
int main() {
scanf("%d",&n);
scanf("%s",s+1);
scanf("%s",t+1);
if (n<=2) {
int ans=0;
fo(i,1,n) ans+=(s[i]!=t[i]);
printf("%d\n",ans);
return 0;
}
s[0]=(s[1]=='1' ?'0' :'1' );
t[0]=(t[1]=='1' ?'0' :'1' );
pre(s,ps,ps0,ps1,ps2);
pre(t,pt,pt0,pt1,pt2);
int ans=n*n*10;
fo(i,1,pt2) if (ps[ps1].second==pt[i].second) {
int bs=ps1-max(0,i-pt1), be=ps2+max(0,pt2-(i+ps2-ps1)), bt=i-(ps1-bs), len=be-bs+1;
int ans1=0;
fo(j,1,len) ans1+=abs(ps[bs+j-1].first-pt[bt+j-1].first);
ans=min(ans,ans1);
}
printf("%d\n",ans);
}