传送门
先令
′
L
′
'L'
′L′为
0
0
0,
′
R
′
'R'
′R′为
1
1
1,然后本题就可以无脑dp,设
d
p
[
i
]
[
j
]
[
k
]
[
h
]
[
w
]
dp[i][j][k][h][w]
dp[i][j][k][h][w]表示考虑到字符串第
i
i
i位,
s
[
1
]
=
j
,
s
[
2
]
=
k
s[1]=j,s[2]=k
s[1]=j,s[2]=k,
s
[
i
−
1
]
=
h
,
s
[
i
]
=
w
s[i-1]=h,s[i]=w
s[i−1]=h,s[i]=w的时候对应的最小改变字符数(使得前
i
i
i个字符合法,头尾不保证合法,在最后的时候对
n
n
n特判)。
方程有亿点点不好写,大概也就讨论了十几种情况吧,下面给出具体的代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
const int inf = 2147483647;
char s[maxn],g[2];
int dp[maxn][2][2][2][2];//
int main(){
int t;
g[0]='L',g[1]='R';
scanf("%d",&t);
while(t--){
int n;
scanf("%d%s",&n,s+1);
for(int i=1;i<=n;++i){
for(int j=0;j<2;++j){
for(int k=0;k<2;++k){
for(int h=0;h<2;++h){
for(int w=0;w<2;++w){
dp[i][j][k][h][w]=inf/4;
}
}
}
}
}
for(int i=0;i<2;++i){
for(int j=0;j<2;++j){
dp[2][i][j][i][j]=(s[1]!=g[i])+(s[2]!=g[j]);
}
}
for(int i=2;i<n-1;++i){
//00
for(int j=0;j<2;++j)for(int k=0;k<2;++k)dp[i+1][0][1][j][k]=min(dp[i+1][0][1][j][k],dp[i][0][0][j][k]+(s[i+1]!=g[1]));
//01
for(int j=0;j<2;++j)for(int k=0;k<2;++k){
dp[i+1][1][0][j][k]=min(dp[i+1][1][0][j][k],dp[i][0][1][j][k]+(s[i+1]!=g[0]));
dp[i+1][1][1][j][k]=min(dp[i+1][1][1][j][k],dp[i][0][1][j][k]+(s[i+1]!=g[1]));
}
//10
for(int j=0;j<2;++j)for(int k=0;k<2;++k){
dp[i+1][0][0][j][k]=min(dp[i+1][0][0][j][k],dp[i][1][0][j][k]+(s[i+1]!=g[0]));
dp[i+1][0][1][j][k]=min(dp[i+1][0][1][j][k],dp[i][1][0][j][k]+(s[i+1]!=g[1]));
}
//11
for(int j=0;j<2;++j)for(int k=0;k<2;++k){
dp[i+1][1][0][j][k]=min(dp[i+1][1][0][j][k],dp[i][1][1][j][k]+(s[i+1]!=g[0]));
}
}
//00
dp[n][0][1][0][0]=min(dp[n][0][1][0][0],dp[n-1][0][0][0][0]+(s[n]!=g[1]));
dp[n][0][1][0][1]=min(dp[n][0][1][0][1],dp[n-1][0][0][0][1]+(s[n]!=g[1]));
dp[n][0][1][1][0]=min(dp[n][0][1][1][0],dp[n-1][0][0][1][0]+(s[n]!=g[1]));
//01
dp[n][1][0][0][1]=min(dp[n][1][0][0][1],dp[n-1][0][1][0][1]+(s[n]!=g[0]));
dp[n][1][0][1][0]=min(dp[n][1][0][1][0],dp[n-1][0][1][1][0]+(s[n]!=g[0]));
dp[n][1][0][1][1]=min(dp[n][1][0][1][1],dp[n-1][0][1][1][1]+(s[n]!=g[0]));
dp[n][1][1][0][0]=min(dp[n][1][1][0][0],dp[n-1][0][1][0][0]+(s[n]!=g[1]));
dp[n][1][1][0][1]=min(dp[n][1][1][0][1],dp[n-1][0][1][0][1]+(s[n]!=g[1]));
//10
dp[n][0][0][1][0]=min(dp[n][0][0][1][0],dp[n-1][1][0][1][0]+(s[n]!=g[0]));
dp[n][0][0][1][1]=min(dp[n][0][0][1][1],dp[n-1][1][0][1][1]+(s[n]!=g[0]));
dp[n][0][1][1][0]=min(dp[n][0][1][1][0],dp[n-1][1][0][1][0]+(s[n]!=g[1]));
dp[n][0][1][0][0]=min(dp[n][0][1][0][0],dp[n-1][1][0][0][0]+(s[n]!=g[1]));
dp[n][0][1][0][1]=min(dp[n][0][1][0][1],dp[n-1][1][0][0][1]+(s[n]!=g[1]));
//11
dp[n][1][0][0][1]=min(dp[n][1][0][0][1],dp[n-1][1][1][0][1]+(s[n]!=g[0]));
dp[n][1][0][1][0]=min(dp[n][1][0][1][0],dp[n-1][1][1][1][0]+(s[n]!=g[0]));
dp[n][1][0][1][1]=min(dp[n][1][0][1][1],dp[n-1][1][1][1][1]+(s[n]!=g[0]));
int ans=inf;
for(int i=0;i<2;++i)for(int j=0;j<2;++j)for(int k=0;k<2;++k)for(int w=0;w<2;++w)ans=min(ans,dp[n][i][j][k][w]);
printf("%d\n",ans);
}
}
那有没有更简单的方法呢,容易 发现其实只要序列中出现了连续的三个相同字母,那么就不合法,因此只要通过改变一些字母使得序列中没有三个连续的字母即可,注意到序列是环形的。
于是考虑贪心,如果所有字母都相同需要特判,答案是 ⌈ n 3 ⌉ \lceil \frac n3\rceil ⌈3n⌉,否则的话考虑所有连续的相同字母构成的段的长度假设是 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak,那么答案是 ∑ i = 1 k ⌊ a i 3 ⌋ \sum_{i=1}^k\lfloor\frac{a_i}3\rfloor ∑i=1k⌊3ai⌋。
输出答案即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
const int inf = 2147483647;
char s[maxn<<1];
vector<int>g;
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d%s",&n,s+1);
bool diff=false;
g.clear();
for(int i=1;i<=n;++i){
s[n+i]=s[i];
if(i>1 && s[i]!=s[i-1])diff=true;
}
if(!diff){
printf("%d\n",(n+2)/3);
continue;
}
int cnt=0;
g.push_back(0);
for(int i=2;i<=n+1;++i){
if(s[i]!=s[i-1]){
for(int j=i;j<=i+n-1;++j){
if(j==i || j>i&&s[j]==s[j-1]){
g.back()++;
}else g.push_back(1);
}
break;
}
}
int ans=0;
for(int i=0;i<g.size();++i)ans+=g[i]/3;
printf("%d\n",ans);
}
}