【AtCoder Grand 030E】Less than 3 题解

题目大意

  给定两个长度为 $n$ 的 01 串 $s,t$,每个串都不会有连续三个相同的字符。现在每次操作可以将 $s$ 的一位反转,但反转之后也要保证没有连续三个相同的字符,求最少的步数使得 $s$ 变成 $t$。

  $n \le 5000$
  2s

题解

  题解这个模型转化非常 nice。

  给 $0$ 和 $1$ 之间画一条红线,给 $1$ 和 $0$ 之间画一条蓝线,这样这个 01 串就变成了:

  • 有很多红蓝相间的线;(开头和结尾可以补充无限条红蓝相间的线)
  • 相邻的线间隔为 1 或 2;
  • 每次操作可以将一条线往左或者往右移 1 位,不能与别的线重合,线的间隔仍然要为 1 或 2。

  可以发现这样就变成了线的匹配问题。$O(n)$ 枚举 $s$ 串开头的线匹配 $t$ 的哪一条线,然后再 $O(n)$ 算出匹配代价即可,总共 $O(n^2)$。整个过程用些数据结构或者 trick 可以优化成 $O(n)$,不过意义不大。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#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);
}