Educational Codeforces Round 30 B【前缀和+思维/经典原题】
You are given a string s consisting only of characters 0 and 1. A substring [l, r] of s is a string slsl + 1sl + 2... sr, and its length equals tor - l + 1. A substring is called balanced if the number of zeroes (0) equals to the number of ones in this substring.
You have to determine the length of the longest balanced substring of s.
The first line contains n (1 ≤ n ≤ 100000) — the number of characters in s.
The second line contains a string s consisting of exactly n characters. Only characters 0 and 1 can appear in s.
If there is no non-empty balanced substring in s, print 0. Otherwise, print the length of the longest balanced substring.
8
11010111
4
3
111
0
In the first example you can choose the substring [3, 6]. It is balanced, and its length is 4. Choosing the substring [2, 5] is also possible.
In the second example it's impossible to find a non-empty balanced substring.
【题意】:一个01字符串,求出现0、1出现次数相等的最长子串的长度。
【分析】:
change array a[]==0 to -1.find prefix[]=prefix sum
find two indices of maximum difference for which it is same value.
take example.
1 0 0 1
1 -1 -1 1
prefix[]=1 0 -1 0
思路:如果某子串中0与1的个数相等那么该子串的代数和肯定等于0。如果最长子串是从开头开始的那么当代数和等于0出现的最后边的位置即最长子串的末尾,该子串即最长子串;如果不是从开头开始的,那么最长子串即代数和相等的两个位置的距离最长的那个子串。用一个数组mp[]来记录加和首次出现的位置,如果该加和还没有出现过就记录下来此时位置,由于是从前往后遍历的,则首次记录的位置肯定是最前边的位置,那么同等加和出现的位置越往后则两者距离就越长。详细请看代码即代码旁的注释。
【代码】:
#include <bits/stdc++.h> using namespace std; map<int,int> mp; int a[100000+10];//前缀和数组 int main() { int n; char c; cin>>n; int sum=0; for(int i=1;i<=n;i++) { scanf(" %c",&c); if(c=='1') sum++; else sum--; a[i]=sum;//将前缀和装入a数组 if(!mp[sum])//如果此时的代数和之前没出现过则记录下来此时的位置 mp[sum]=i; } mp[0]=0;//前缀和位置数组 int ans=0; for(int i=1;i<=n;i++) ans=max(ans,i-mp[a[i]]);//如果此代数和之前出现过就计算一下当前位置距离首次此代数和出现位置有多远,然后再与ans比较取最大 cout<<ans; return 0; }
遇见一个1,sum++,遇见0,sum--;记录每一个的sum的位置,,dis[sum]=i; 初始的时候把dis全部初始化为-1,如果当前dis[sum]= -1证明这个sum第一次出现,记录其出现的位置。如果当前did[sum]!=-1,那么证明这个sum,之前出现过,那么从之前那个位置到现在这个位置,中间的和为0,sum+0=sum 证明中间是一部分平衡01串,然后一直更新,最后的结果就是最长的平衡01串。类似组合数学中做鸽巢定理一道题的思路。因为有负数的存在,所以每个sum+1e5。
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long using namespace std; char num[2000000]; //存储01数字串 int dis[2000002]; //记录代数和首次出现的位置 int main() { //freopen("lalala.text","r",stdin); while(~scanf("%s",num)) { memset(dis,-1,sizeof(dis)); //初始化为-1 int len=strlen(num); int sum=0,mm=0; //sum记录从开头到当前位置的代数和,mm代表此时最长子串的长度 for(int i=0; i<len; i++) { if(num[i]=='1') //如果是1就+1 sum++; else if(num[i]=='0') //如果是0就-1 sum--; if(sum==0) //如果代数和是0就说明该最长子串是从头开始的 { mm=max(mm,i+1); //只要出现0就比一下最长长度 continue; } if(dis[sum+1000000]==-1) //为了以防出现负数sum+1000000保证下标始终为正;如果此时的代数和之前没出现过则记录下来此时的位置 dis[sum+1000000]=i; else //如果此代数和之前出现过就计算一下当前位置距离首次此代数和出现位置有多远,然后再与mm比较取最大 { mm=max(mm,i-dis[sum+1000000]); } } printf("%d\n",mm); } return 0; }