题目链接:传送门
题意:
求一个最长的子串,使得这个子串可以切割成两个串,左边的串0的数量大于1的数量,右边的串1的数量大于0的数量。
分析:
我们可以首先预处理出从开头到第i个位置0的数量大于1的串的最长的长度dp1[i],同理设dp2[i]表示从第i个位置到结尾的1的数量大于0的数量的最长长度。那么我们可以发现当如果 [i,j]为0的数量大于1的数量的最长长度那么
str[i-1]一定等于1,同理如果 [i,j]为1的数量大于0的数量的最长长度,那么str[j+1]一定等于0,然后我们用-1替代0,那么用他们的和的正负来表示0,1的数量关系。
预处理的过程:
设cur表示从开头到i的元素的和:
1.cur<0 : dp1[i] = i + 1;
2.cur>0 : dp1[i] = i - pos[cur+1]表示cur+1最早出现的位置。
同理可以推出dp2.
代码如下:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e6+10;
char str[maxn];
int sum0[maxn],sum1[maxn];
int head[maxn];
int main() {
while(~scanf("%s",str)) {
int len=strlen(str);
memset(sum1,0,sizeof(sum1));
memset(sum0,0,sizeof(sum0));
int cur=0;
memset(head,-1,sizeof(head));
for(int i=0; i<len; i++) {
if(str[i]=='0') cur+=-1;
else cur+=1;
if(cur<0) sum0[i]=i+1;
else {
if(head[cur+1]!=-1)
sum0[i]=i-head[cur+1];
else {
head[cur]=i;
sum0[i]=0;
}
}
}
memset(head,-1,sizeof(head));
cur=0;
for(int i=len-1; i>=0; i--) {
if(str[i]=='0') cur+=-1;
else cur+=1;
if(cur>0) sum1[i]=len-i;
else {
if(head[-(cur-1)]!=-1) {
sum1[i]=head[-(cur-1)]-i;
} else {
sum1[i]=0;
head[-(cur)]=i;
}
}
}
int ans = 0;
for(int i=0; i<len-1; i++) {
if(sum0[i]>0&&sum1[i+1]>0)
ans = max(ans,sum0[i]+sum1[i+1]);
}
printf("%d\n",ans);
}
return 0;
}