1. 程式人生 > >51nod 1391 01串【線段樹,思維】

51nod 1391 01串【線段樹,思維】

題意:

給定一個01串S,求出它的一個儘可能長的子串S[i..j],滿足存在一個位置i<=x < j, S[i..x]中0比1多,而S[x + 1..j]中1比0多。求滿足條件的最長子串長度。
Input

一行包含一個只由0和1構成的字串S。 S的長度不超過1000000。

Output

一行包含一個整數,表示滿足要求的最長子串的長度。

分析:

比較裸的線段樹題目。
求一下字首和,然後RMQ查詢【1,x-1】區間大於sum[x]的最小下標L,【x+1,n】區間找大於sum[x]的最大下標R,那麼合法長度就是R-L+1。
時間複雜度O(nlogn)

這題還有O(n)的做法。
先從前往後遍歷一遍,此時字首和假如小於零,則標記長度即可
若此時字首和大於零,則往前找到第一個出現字首之和+1的地方,減去即可。
這裡不難證明,第一次出現的情況比為當前情況的最優解
同理 字尾也一樣。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef pair<int,int>pii;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=1000000+7;
char s[N];
int a[N],f[N],h[N],g[N];
int main() {
    //freopen("in.txt","r",stdin);
    scanf("%s",s);
    int n=strlen
(s); for(int i=0;i<n;i++){ if(s[i]=='0')a[i]=-1; else a[i]=1; } int sum=0; memset(h,-1,sizeof(h)); for(int i=0;i<n;i++){ sum+=a[i]; if(sum<0)f[i]=i+1; else{ if(h[sum+1]>=0){ f[i]=i-h[sum+1]; } else
{ h[sum]=i; f[i]=0; } } } sum=0; memset(h,-1,sizeof(h)); for(int i=n-1;i>=0;i--){ sum+=a[i]; if(sum>0)g[i]=n-i; else{ if(h[-(sum-1)]>=0)g[i]=h[-(sum-1)]-i; else h[-sum]=i,g[i]=0; } } int ans=0; for(int i=0;i<n-1;i++){ if(f[i]&&g[i+1])ans=max(ans,f[i]+g[i+1]); } printf("%d\n",ans); return 0; }