51nod1391–01串
题意
求一段区间(i,j),保证区间内存在一个i<=x<ji<=x<j,使[i,x]区间内0的个数比一多,[x+1,j]区间内1的个数比0多,求区间的最长长度
做法
我们如果可以o(n)预处理出每个位置最多向前多少,最多向后多少,就可以做出这个题了, 类似一般01串的题,肯定是要转换成-1,1来求个前缀和,如果当前下标为x,那么以x为结尾的区间[z,x]区间和肯定是小于0的,设sum[x]为前缀和,那么如果sun[x]<0表示从头到现在的0的个数大于1,那么要预处理的数组a[x]=x,如果sum[x]>=0,我们就要找最早出现的>sum[x]的数字,那么这个数是多少呢,如果大于sum[x]的数字出现过,那么sum[x]+1肯定就出现过,而且sum[x]+1肯定是最早出现的,因为sum[x]+2,sum[x]+3肯定出现在sum[x]+1之后(前缀和,感性理解一下,变化一定是连续的),所以我们只要看之前是否出现过sum[x]+1就可以了,每次扫描之后如果sum[x]之前没有出现过,就让sum[x]的最早出现位置变为x,保证区间最长。这样向前的预处理就做完了,重点是最早出现过的大于sum[x]的数字一定是sum[x]+1.我们处理向后的方法与向前完全一样,只不过用后缀和。最后o(n)枚举分界点就可以了!
代码
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<<x<<endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
typedef long long ll;
const int maxn = 1e6+5;
int sum1[maxn];
int sum2[maxn];
int a[maxn];
int b[maxn];
int pos1[maxn];
int pos2[maxn];
char str[maxn];
int main()
{scanf("%s",str+1);int n=strlen(str+1);for(int i=1;i<=n;i++){if(str[i]=='0') sum1[i]=sum1[i-1]-1;else sum1[i]=sum1[i-1]+1;}for(int i=n;i>=1;i--){if(str[i]=='1') sum2[i]=sum2[i+1]-1;else sum2[i]=sum2[i+1]+1;}for(int i=1;i<=n;i++) pos1[i]=-1,pos2[i]=-1;for(int i=1;i<=n;i++){if(sum1[i]<0) a[i]=i;else{if(pos1[sum1[i]+1]!=-1) a[i]=i-pos1[sum1[i]+1];else a[i]=0;if(pos1[sum1[i]]==-1) pos1[sum1[i]]=i;}}for(int i=n;i>=1;i--){if(sum2[i]<0) b[i]=n-i+1;else{if(pos2[sum2[i]+1]!=-1) b[i]=pos2[sum2[i]+1]-i;else b[i]=0;if(pos2[sum2[i]]==-1) pos2[sum2[i]]=i;}}int ans=0;for(int i=1;i<=n-1;i++) if(a[i]!=0&&b[i+1]!=0) ans=max(ans,a[i]+b[i+1]);printf("%d\n",min(ans,n));return 0;
}