【机试备考】Day19-最长平衡串 | 前缀和

题目

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)

猜你喜欢

转载自blog.csdn.net/qq_43417265/article/details/113768614