动态规划--最大连续子串问题

最大连续子串问题

   首先,要明白子串和子序列的区别。

1)子串(Substring)是串的一个连续的部分;

2)子序列(Subsequence)则是从不改变序列的顺序,而从序列中去掉任意的元素而获得的新序列;(关于求最长子序列的算法见博客:动态规划(5)-字符串相似度算法))

       最大连续子串问题是给定一组元素,求使得连续子串的值最大,比如最大连续和子串、最大连续乘积子串。这类问题可以用动态规划求解。下面举例说明如何求解这类问题。

最大连续和子串

例子:输入一个整型数组,数组里有正数也有负数。数组中一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

算法1:时间复杂度O(n*n)
[java]  view plain copy
  1. <span style="font-size:18px">maxsofar=pData[0];  
  2. for i=[0,n)  
  3.    sum=0;  
  4.    for j=[i,n){  
  5.       sum+=pData[j];  
  6.  maxsofar=max(maxsofar,sum);  
  7.    }//end for</span>  
算法2:动态规划,时间复杂度O(n)
1)最优子结构
记f[i]表示以第i个数字结尾的子数组的最大和,那么要求max{f[i]},其中0<=i<n。

假设f[i-1]是i-1时的最优解,则如何求f[i]时的最优解呢?

此时f[i]面临2种选择:一是与前面子数据相加,二是不与前面子数据相加,即f[i]=pData[i]。具体的:

当f[i-1]<=0时,如果把负数与pData[i]相加,得到的结果比pData[i]本身还要小,所以此时f[i]=pData[i]
当f[i-1]>0时,则pData[i]与正数相加,得到的结果比pData[i]本身大,所以此时f[i]=f[i-1]+pData[i]
2)迭代解
当i=0或者f[i-1]<=0时,
f[i]=pData[i]
当i!=0并且f[i-1]>0时,

f[i]=f[i-1]+pData[i]

[java]  view plain copy
  1. public int maxSubArray(int[] pData)throws Exception{  
  2.    //输入控制  
  3.    if(pData==null)  
  4.      throw new Exceptin("input can not be null");  
  5.    n=pData.length;  
  6.    if(n==0)  
  7.       throw new Exceptin("have no data,must have one element");  
  8.    //只有一个数字的情况  
  9.    if(n==1)  
  10.       return pData[0];  
  11.         
  12.    //初始化   
  13.    int[] f=new int[n]; //用数组f保存计算过程中的值  
  14.    f[0]=pData[0];  
  15.    int maxsofar=f[0];   //用maxsofar保留当前最大值  
  16.    
  17.    for(int i=1;i<n;i++){  
  18.       if(f[i-1]<0)  
  19.         f[i]=pData[i];  
  20.       else  
  21.         f[i]=f[i-1]+pData[i];  
  22.       //记录最大值   
  23.       maxsofar=max(maxsofar,f[i]);  
  24.    }//end for  
  25.      
  26.    return maxsofar;  
  27. }//end maxSubArray()  

相关题目:如果希望查找一个总和最接近某一给定实数(如t)的子向量,假设函数absolute(a)用于求a的绝对值,则
absoluteT=absolute(t)
f[i]的值应该满足以下条件:
min{ absolute( absolute(f[i-1]+pData[i]) - absoluteT ), absolute( absolute(pData[i]) - absoluteT) } 
所以,有以下递推关系
absolute( absolute(f[i-1]+pData[i]) - absoluteT )>= absolute( absolute(pData[i]) - absoluteT)时,f[i]=pData[i]
absolute( absolute(f[i-1]+pData[i]) - absoluteT ) < absolute( absolute(pData[i]) - absoluteT)时,f[i]=f[i-1]+pData[i]

同时用nearT记录f中最接近t的那个总和

最大乘积连续子串

例子:问题:题目描述:给一个浮点数序列,取最大乘积连续子串的值,例如 -2.5,4,0,3,0.5,8,-1,则取出的最大乘积连续子串为3,0.5,8。也就是说,上述数组中,3 0.5 8这3个数的乘积3*0.5*8=12是最大的,而且是连续的。

【算法一】分析:动态规划类似于上述解法,其递归解如下:

当i=0时,f[i]=pData[i]
当i!=0时,

1)当f[i-1]>1时,f[i-1]=f[i-1]*pData[i];

2)当0<f[i-1]<1时,

当pData[i]>=0时,f[i-1]=pData[i];

当pData[i]<0时,f[i-1]=f[i-1]*pData[i];

3)当f[i-1]=0时,

当pData[i]>0时,f[i-1]=pData[i];

当pData[i]<=0时,f[i-1]=f[i-1]*pData[i];

4)当f[i-1]<0时,

当pData[i]>0时,f[i-1]=pData[i];

当pData[i]<0时,f[i-1]=f[i-1]*pData[i];

【算法二】递归公式如下(参考自July最大乘积子串):

假设数组为a[],考虑到可能存在负数的情况,我们用Max来表示以a结尾的最大连续子串的乘积值,用Min表示以a结尾的最小的子串的乘积值,那么状态转移方程为:
       Max=max{a, Max[i-1]*a, Min[i-1]*a};
       Min=min{a, Max[i-1]*a, Min[i-1]*a};
    初始状态为Max[1]=Min[1]=a[1]。

[cpp]  view plain copy
  1. double func(double *a,const int n)    
  2. {    
  3.     double *maxA = new double[n];    
  4.     double *minA = new double[n];    
  5.     maxA[0] = minA[0] =a[0];    
  6.     double value = maxA[0];    
  7.     for(int i = 1 ; i < n ; ++i)    
  8.     {    
  9.         maxA[i] = max(max(a[i],maxA[i-1]*a[i]),minA[i-1]*a[i]);    
  10.         minA[i] = min(min(a[i],maxA[i-1]*a[i]),minA[i-1]*a[i]);    
  11.         value=max(value,maxA[i]);    
  12.     }    
  13.     return value;    
  14. }  


【拓展】给定一个长度为N的整数数组,只允许用乘法,不能用除法,计算任意(N-1)个数的组合中乘积最大的一组,并写出算法的时间复杂度。

算法:如果允许对数组排序的话,先排序数组,然后从第一个正数开始,转换为求“最大连续乘积子串”的问题。时间复杂度O(n+nlogn)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值