[getLongestLength] 加和为0的最长子串长度

点击这里查看原文

假设一个数组仅仅由1和-1组成,求该数组的和为0的最长子串的长度。
例如:
{1,-1,1,-1,1,1,1} 输出:4.
昨天机试的时候做到这道题,不会做,今天思考一下。

普通的解法

枚举所有的子串,然后得到和为0的最长的长度,复杂度是O(n*n),这个应该会超时,昨天只能保存,不能运行,我猜这样应该不行,需要下面方法。其实看到这题,我想到的是最长回文子串这道题,仔细想想,还是挺像的,该搞一个传送门的。

正确的解法

解法:动态规划,dp0[i]表示以i结尾的加和为0 得到最长子串长度,dp1[i] 表示以i结尾的加和为1的最长子串长度,dp2[i]表示以i结尾的加和为-1 的最长子串长度。于是可以得到下面的递推关系:

上面这段话摘自那个博客,n的平方复杂度不行,那大概就是nlogn或者n,需要考虑dp,然后分析,数组只有1或者-1,然后需要获得结果为0的长度,所以碰到1,我们希望能知道前面和为-1的最长,遇到-1,我们需要知道和为1的最长,所以我们就需要维护到当前位置,以当前位置结尾的和为0,1,-1的最长长度,然后思考怎么递推。显然,1 + 0 = 1, 1 + (-1) = 0,转移到1和0,很容易,转移到-1,怎么处理,什么结果加上1能得到结果是-1,当然是-2,但是我没维护和为2的,这就需要巧妙的思想。

利用当前段的结果为0,加上这段之前的结果为-1,这样就能导出当前段为-1的,这样的思想很巧妙,仔细想想怎么回事,是不是有点kmp,马拉车算法之类的思想。

好了,贴一下那位大神的代码:

#include<iostream>
#include<vector>
using namespace std;
int getLongestLength(int array[],int size){
    if(size<=1) return 0;
    vector<int>dp0(size);
    vector<int>dp1(size);
    vector<int>dp2(size);
    dp0[0]=0;
    if(array[0]==1){
        dp1[0]=1;
        dp2[0]=0;
    }else{
        dp1[0]=0;
        dp2[0]=1;
    }
    int result =0;
    for(int i =1;i<size;i++){
        if(array[i]==1){
            dp0[i]=dp2[i-1]==0?0:dp2[i-1]+1;
            dp1[i]=dp0[i-1]==0?1:dp0[i-1]+1;
            dp2[i]=dp0[i]==0?0:dp2[i-dp0[i]]+dp0[i];
        }else{
            dp0[i]=dp1[i-1]==0?0:dp1[i-1]+ 1;
            dp1[i]=dp0[i]==0?0:dp1[i-dp0[i]]+dp0[0];
            dp2[i]=dp0[i-1]==0?0:dp0[i-1]+1;
        }
        if(result<dp0[i]) result  =dp0[i];
    }

    return result;
}
int main(){
    int nums[]={-1,1,-1,1};
    cout<<getLongestLength(nums,4);
}

我认为也需要判断一下dp2[i-dp0[i]]的结果不为0,才行。没有画图,不知道解释清楚没有。

还可以这样做,维护前缀和s,i<j, s(j) = s(i)的时候,可以知道i + 1 到j和为0,即可满足要求。这个好像容易理解一些。

百度搜索1,-1,最长字串 可以找到解法。延伸到数组里面只有0和1,比如只有a和b,求最长子串a和b的个数相等之类的。还有什么变形,想出来再记下来吧。

int getLongestString(vector<int> & v) {
    int n = v.size();
    if(n < 2) return 0;
    map<int, int> m;
    int s = 0;
    int res = 0;
    for(int i = 0; i < n; i++) {
        s += v[i];
        if(s == 0) {
            res = max(res, i + 1); continue;
        }
        if(!m.count(s)) {
            m[s] = i;
        } else {
            int len = i - m[s];
            res = max(res, len);
        }
    }
    return res;
}

转载于:https://www.cnblogs.com/y119777/p/5851025.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值