51nod 1391 01串 (dp+hash)

题目链接:传送门 


题意:

求一个最长的子串,使得这个子串可以切割成两个串,左边的串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;
}


你可能感兴趣的:(51nod 1391 01串 (dp+hash))