题目
BUPT 2018 计算机 ProblemD
给定只含01的字符串,找出最长平衡子串的长度(平衡串:包含0和1的个数相同)
输入描述
多组测试数据输入。
输入一串01字符串,字符串长度最大为100000。
输出描述
请输出最长的平衡子串的长度。
示例
输入
101011000
输出
8
题解
刚开始理解错题意了,以为一定是要从头开始的子串,后来看了题解发现是任意位置的子串都可,暴力破解就是O( n 2 n^2 n2),会超时,于是学习了下面这种解法
前缀和O(n)解法
思路:把这里的0换成-1,然后利用前缀和思想找最长平衡串
模拟过程:
- 前缀和:d[i]代表s前i位之和
- 用到这道题上找最长平衡串就是找
d[i]==0
的最大i或者d[i]-d[j]==0
的最大i,j之差
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
---|---|---|---|---|---|---|---|---|
s | 1 | 0 | 0 | 0 | 1 | 0 | 1 | – |
d | – | 1 | 0 | -1 | -2 | -1 | -2 | -1 |
- m[i]表示d[i]与其起始位置的映射关系
d | 1 | -1 | -2 |
---|---|---|---|
m | 1 | 3 | 4 |
所以你找到最长平衡串了吗?应该是s[3]~s[6]长度为4的子串,也刚好是d[7]-d[3]==0
最大i,j之差的点
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s;
while(cin>>s)
{
int res=0;
int d[s.length()+1];//记录前缀和
d[0]=0;
map<int,int>m;//用map进行d[i]与其最开始位置的映射
for(int i=1;i<=s.length();i++)
{
/*计算前缀和*/
int tmp=s[i-1]=='0'?-1:1;//0转成-1
d[i]=d[i-1]+tmp;//前缀和关键
/*最长平衡串判定*/
if(d[i]==0)//d[i]为0,说明从头到i就是平衡串
res=max(res,i);
else
{
if(m[d[i]]==0)//d[i]不是0且开始位置还没有被记录过,则记录开始位置
m[d[i]]=i;
else//开始位置被记录过,说明和上次位置之间的子串可构成平衡串
res=max(res,i-m[d[i]]);//更新res
}
}
cout<<res<<endl;
}
}
真的是相当的妙啊
小结
前缀和
举个例子很好理解,长为n的a数组,一共有m个询问,每次问[l,r]范围内的数字之和是多少?
这里d数组就是a数组的前缀和,即d[3]=a[1]+a[2]+a[3]
如果要求[3,5]区间内的数字和的话直接用d[5]-d[3-1]
就可以得到了,时间复杂度为O(n)