【51NOD 1391 01串】前缀和预处理

51nod1391–01串
题意
求一段区间(i,j),保证区间内存在一个 i <= 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;
}

猜你喜欢

转载自blog.csdn.net/qq_38891827/article/details/82599657
今日推荐