题目链接:【A. Alternative Thinking】
输入一串长为n的01串,翻转其中的一段,所谓的翻转就是0变1,1变0,求最终最长01交替的序列长度
样例1:10000011 翻转后得到 10100011 最终的长度就是5 (红色部分)
方法一:数学找规律
原先相邻的变化后仍然相邻,所以我们可以先算出原本就相邻的最长是多少,一个子串翻转后,我们的要求是最长交替01串至少跟原先是一样的,不可能减少,所以一共就只有两种情况
1、相同的数字相邻的总和只有两个 如10010(最初的最长交替串很显然是4)我们想要增加就只能将第三个0改成1,这要引起后边部分的同时改变,不然最终交替串会减少,最后一定是变成10101,长度比原先多了1
2、相同的数字相邻的总和>2(00,11加在一起),那前后已经交替出现的就不要管它了,将相同部分反转,结果必然比原先多2,如:10011=>10101
<span style="font-size:14px;"> #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
char s[100005];
int main()
{
int n, a=0, b=0;
scanf("%d%s", &n, s+1);
if(s[n]=='0') s[n+1]=1;
else s[n+1]=0;
for(int i=1; i<=n; i++)
{
if(s[i] == s[i+1]) a++;
else b++;
}
printf("%d\n", b+min(2, a));
return 0;
}</span>
方法二:dp
dp[ i ][ j ][ k ]
i表示querylen=i
0<=j<=2,0表示没有翻转,1表示到i为止正在翻转,2表示已经翻转结束
0<=k<=1,0表示位置i是0,1表示位置i是1
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int inf =1e5+10;
char s[inf];
int dp[inf][3][2];
int main()
{
int n;
scanf("%d%s", &n, s);
for(int i=0; i<n; i++)
{
for(int j=0; j<3; j++)
{
for(int k=0; k<2; k++)
{
dp[i+1][j][k] = dp[i][j][k];
}
}
for(int j=0; j<3; j++)
{
for(int k=0; k<2; k++)
{
int c = s[i]-'0';
if(k != c)
{
dp[i+1][j][c] = max(dp[i+1][j][c], dp[i][j][k]+1);
}
else
{
if(j < 2)
{
dp[i+1][j+1][c] = max(dp[i+1][j+1][c], dp[i][j][c]+1);
}
}
}
}
}
int ans=0;
for(int i=0; i<3; i++)
{
for(int j=0; j<2; j++)
{
ans = max(ans, dp[n][i][j]);
}
}
printf("%d\n", ans);
return 0;
}