最大连续乘积子串_给一个浮点数序列,取最大乘积连续子串-程序员宅基地

技术标签: 算法  

  1. 题目描述:


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


    提醒:此最大乘积连续子串与最大乘积子序列不同,请勿混淆,前者子串要求连续,后者子序列不要求连续。也就是说:最长公共子串(Longest CommonSubstring)和最长公共子序列(LongestCommon Subsequence,LCS)的区别:



  2. 子串(Substring)是串的一个连续的部分,

  3. 子序列(Subsequence)则是从不改变序列的顺序,而从序列中去掉任意的元素而获得的新序列;


    更简略地说,前者(子串)的字符的位置必须连续,后者(子序列LCS)则不必。比如字符串“ acdfg ”同“ akdfc ”的最长公共子串为“ df ”,而它们的最长公共子序列LCS是“ adf ”,LCS可以使用动态规划法解决。


    解答:


    解法一、 穷举,所有的计算组合:


    或许,读者初看此题,自然会想到最大乘积子序列问题类似于最大子数组和问题,可能立马会想到用最简单粗暴的方式:两个for循环直接轮询。


    double max = 0;
    double start = 0;
    double end = 0;
    for (int i = 0; i < num; i++)
    {
       
    double x = arrs[i];
       
    for (int j = i + 1; j < num; j++)
        {
            x
    *= arrs[j];
           
    if (x > max)
            {
                max
    = x;
                start
    = arrs[i];
                end
    = arrs[j];
            }
        }
    }


    解法二、 虽说类似于最大子数组和问题,但实际上具体处理起来诸多不同。为什么呢,因为乘积子序列中有正有负也还可能有0。我们可以把问题简化成这样:数组中找一个子序列,使得它的乘积最大;同时找一个子序列,使得它的乘积最小(负数的情况)。因为虽然我们只要一个最大积,但由于负数的存在,我们同时找这两个乘积做起来反而方便。也就是说,不但记录最大乘积,也要记录最小乘积。So,我们让


  4. maxCurrent表示当前最大乘积的candidate,
  5. minCurrent反之,表示当前最小乘积的candidate,
  6. 而maxProduct则记录到目前为止所有最大乘积candidates的最大值。 (以上用candidate这个词是因为只是可能成为新一轮的最大/最小乘积)


    由于空集的乘积定义为1,在搜索数组前,maxCurrent,minCurrent,maxProduct都赋为1。 假设在任何时刻你已经有了maxCurrent和minCurrent这两个最大/最小乘积的candidates,新读入数组的元素x(i)后,新的最大乘积candidate只可能是maxCurrent或者minCurrent与x(i)的乘积中的较大者,如果x(i) < 0导致maxCurrent < minCurrent,需要交换这两个candidates的值。


    当任何时候maxCurrent<1,由于1(空集)是比maxCurrent更好的candidate,所以更新maxCurrent为1,类似的可以更新minCurrent。任何时候maxCurrent如果比最好的maxProduct大,更新maxProduct。


    代码一:


    template <typename Comparable>
    Comparable maxprod(
    const vector<Comparable>&v)
    {
       
    int i;
        Comparable maxProduct
    = 1;
        Comparable minProduct
    = 1;
        Comparable maxCurrent
    = 1;
        Comparable minCurrent
    = 1;
       
    //Comparable t;

       
    for ( i = 0; i < v.size() ; i++)
        {
            maxCurrent
    *= v[i];
            minCurrent
    *= v[i];
           
    if (maxCurrent > maxProduct)
                maxProduct
    = maxCurrent;
           
    if (minCurrent > maxProduct)
                maxProduct
    = minCurrent;
           
    if (maxCurrent < minProduct)
                minProduct
    = maxCurrent;
           
    if (minCurrent < minProduct)
                minProduct
    = minCurrent;
           
    if (minCurrent > maxCurrent)
                swap(maxCurrent, minCurrent);
           
    if (maxCurrent < 1)
                maxCurrent
    = 1;
           
    //if(minCurrent>1)
           
    //    minCurrent =1;
        }
       
    return maxProduct;
    }


    解法三、 本题除了上述类似最大子数组和的解法,也可以直接用动态规划求解(其实,上述的解法一本质上也是动态规划,只是解题所表现出来的具体形式与接下来的解法二不同罢了。这个不同就在于下面的解法二会写出动态规划问题中经典常见的DP方程,而解法一是直接求解)。具体解法如下:


    假设数组为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]。


    C/C++代码一:


    double func(double *a, const int n)
    {
       
    double *maxA = new double[n];
       
    double *minA = new double[n];
        maxA[
    0] = minA[0] = a[0];
       
    double value = maxA[0];
       
    for (int i = 1 ; i < n ; ++i)
        {
            maxA[i]
    = max(max(a[i], maxA[i - 1] * a[i]), minA[i - 1] * a[i]);
            minA[i]
    = min(min(a[i], maxA[i - 1] * a[i]), minA[i - 1] * a[i]);
            value
    = max(value, maxA[i]);
        }
       
    return value;
    }


    C/C++代码二:


    /*
     给定一个浮点数数组,有正有负数,0,正数组成,数组下标从1算起
     求最大连续子序列乘积,并输出这个序列,如果最大子序列乘积为负数,那么就输出-1
     用Max[i]表示以a[i]结尾乘积最大的连续子序列
     用Min[i]表示以a[i]结尾乘积最小的连续子序列  因为有复数,所以保存这个是必须的
    */
    void longest_multiple(double *a, int n)
    {
       
    double *Min = new double[n + 1]();
       
    double *Max = new double[n + 1]();
       
    double *p = new double[n + 1]();
       
    //初始化
       
    for (int i = 0; i <= n; i++)
        {
            p[i]
    = -1;
        }
        Min[
    1] = a[1];
        Max[
    1] = a[1];
       
    double max_val = Max[1];
       
    for (int i = 2; i <= n; i++)
        {
            Max[i]
    = max(Max[i - 1] * a[i], Min[i - 1] * a[i], a[i]);
            Min[i]
    = min(Max[i - 1] * a[i], Min[i - 1] * a[i], a[i]);
           
    if (max_val < Max[i])
                max_val
    = Max[i];
        }
       
    if (max_val < 0)
            printf(
    "%d", -1);
       
    else
            printf(
    "%d", max_val);
       
    //内存释放
        delete [] Max;
        delete [] Min;
    }


    变种


    此外,此题还有另外的一个变种形式,即给定一个长度为N的整数数组,只允许用乘法,不能用除法,计算任意(N-1)个数的组合中乘积最大的一组,并写出算法的时间复杂度。


    我们可以把所有可能的(N-1)个数的组合找出来,分别计算它们的乘积,并比较大小。由于总共有N个(N-1)个数的组合,总的时间复杂度为O(N2),显然这不是最好的解法。


    OK,以下解答来自编程之美


    解法1



    解法2、


    此外,还可以通过分析,进一步减少解答问题的计算量。假设N个整数的乘积为P,针对P的正负性进行如下分析(其中,AN-1表示N-1个数的组合,PN-1表示N-1个数的组合的乘积)。


  7. P为0


    那么,数组中至少包含有一个0。假设除去一个0之外,其他N-1个数的乘积为Q,根据Q的正负性进行讨论:


    Q为0

    说明数组中至少有两个0,那么N-1个数的乘积只能为0,返回0;

    Q为正数

    返回Q,因为如果以0替换此时AN-1中的任一个数,所得到的PN-1为0,必然小于Q;

    Q为负数

    如果以0替换此时AN-1中的任一个数,所得到的PN-1为0,大于Q,乘积最大值为0。


  8. P为负数


    根据“负负得正”的乘法性质,自然想到从N个整数中去掉一个负数,使得PN-1为一个正数。而要使这个正数最大,这个被去掉的负数的绝对值必须是数组中最小的。我们只需要扫描一遍数组,把绝对值最小的负数给去掉就可以了。


  9. P为正数


    类似地,如果数组中存在正数值,那么应该去掉最小的正数值,否则去掉绝对值最大的负数值。

    上面的解法采用了直接求N个整数的乘积P,进而判断P的正负性的办法,但是直接求乘积在编译环境下往往会有溢出的危险(这也就是本题要求不使用除法的潜在用意),事实上可做一个小的转变,不需要直接求乘积,而是求出数组中正数(+)、负数(-)和0的个数,从而判断P的正负性,其余部分与以上面的解法相同。


    在时间复杂度方面,由于只需要遍历数组一次,在遍历数组的同时就可得到数组中正数(+)、负数(-)和0的个数,以及数组中绝对值最小的正数和负数,时间复杂度为O(N)。

     

    Pasted from <https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/05.02.md>

     

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/oanqoanq/article/details/28401311

智能推荐

Android艺术开发探索第三章————View的事件体系(下)_scrollby 导致anr-程序员宅基地

文章浏览阅读5.7k次,点赞2次,收藏6次。Android艺术开发探索第三章————View的事件体系(下) 在这里就能学习到很多,主要还是对View的事件分发做一个体系的了解一.View的事件分发 上篇大致的说了一下View的基础知识和滑动,现在我们再来聊聊一个比较核心的知识点,那就是事件分发了,而且他还是一个难点,我们更加应该掌握,View的滑动冲突一直都是很苦恼的,这里,我们就来一起探索一下1.点击事件的传递规则 我们分_scrollby 导致anr

Mysql学习命令汇总_创建数据库xk2-程序员宅基地

文章浏览阅读702次。连接数据mysql -h主机地址 -P 端口号-u用户名称-p密码1.创建数据库create database 数据库名称;2.删除数据库drop database 数据库名称;3.导入外部数据库source 外部数据库文件所在位置4.切换数据库use 数据库名称5.查看所有数据库名称show databases;6.创建表create table 表名(字段 数据类型,字段 enum(),default,unique,primary key,null,auto_icrement,_创建数据库xk2

Python列表list的常用方法_python中list的各种方法-程序员宅基地

文章浏览阅读722次,点赞2次,收藏7次。Python包含6种数据类型:Number(数字)、String(字符串)、Tuple(元组)、List(列表)、Dictiona(字典)、Set(集合)。列表用于存储任意数目、任意类型的数据集合。列表是内置可变序列,是包含多个元素的有序连续的内存空间,列表一般用[]表示。列表的一些常用方法是与字符串相通的,用法也一样。_python中list的各种方法

C4D OCtane渲染器大师之路笔记(三):调整基本界面设置_octane笔记-程序员宅基地

文章浏览阅读4.9k次。一:界面颜色类型和滑块类型的自定义:操作位置位于octane设置面板内设置选项卡下的其他选项卡在“界面颜色类型”中选择“Octane出厂设置”,材质的颜色选择模块将为RGB模式在“界面颜色类型”中选择“Cinema4D出厂设置”,材质的颜色选择模块将为HSV模式在“滑块类型”中选择“Octane默认”,滑块的造型会和上图有所区别二:GPU的自定义:操作位置位于octane设置面板内设置选项卡下的设置选项卡如果有多张显卡的情况下可以在此界面通过勾选选择使用那些显卡来进行渲染(没有一张选卡被_octane笔记

The beautiful values of the palace---------------------------思维(树状数组+扫描线)-程序员宅基地

文章浏览阅读141次。题意:给定一个n*n的螺旋矩阵,每个方格的权值为当前数字各位之和,现在给出m个宝殿的坐标。再给出q个询问,询问给出的矩形中宝典的权值之和解析:这道题最难的地方就是给定坐标求出螺旋矩阵对应的权值给出对应代码,细节大家自己看ll getval(ll x, ll y, ll n) { ll t = min(min(x, y), min(n - x + 1, n - y + 1)); ll ta = 4 * (t - 1) * (n - t + 1); if (x == n - t + 1) ...

LLMs之LLaMA-2:基于text-generation-webui工具来本地部署并对LLaMA2模型实现推理执行对话聊天问答任务(一键安装tg webui+手动下载模型+启动WebUI服务)、同_text-generation-webui 部署后推理比较慢-程序员宅基地

文章浏览阅读2.7k次,点赞3次,收藏5次。​LLMs之LLaMA-2:基于text-generation-webui工具来本地部署并对LLaMA2模型实现推理执行对话聊天问答任务(一键安装tgwebui+手动下载模型+启动WebUI服务)、同时微调LLaMA2模型(采用Conda环境安装tgwebui+PyTorch→CLI/GUI下载模型→启动WebUI服务→GUI式+LoRA微调→加载推理)之图文教程详细攻略目录基于Text generation web UI工具实现对话聊天大模型应用一、本地部署实现推理_text-generation-webui 部署后推理比较慢

随便推点

springboot启动测试出现Error creating bean with name‘requestMappingHandlerAdapter‘ 的错误_net.minidev.asm.fieldfilter-程序员宅基地

文章浏览阅读5k次。1.使用各工具的版本Java 8Maven 3.3idea 2020.1.4创建springboot用的是spring Initalizr2.出现的错误准备测试springboot数据访问,导入了JDBC场景和数据库的驱动,然后在测试类中测试数据库的时候出现如下的错误:(粘贴的是部分主要错误)java.lang.IllegalStateException: Failed to load ApplicationContextCaused by: org.springframework.b._net.minidev.asm.fieldfilter

2022哈工大(深圳)计算机854考研经验贴|双非跨考|初试367 复试293_854计算机基础难么-程序员宅基地

文章浏览阅读8.2k次,点赞22次,收藏141次。我本科是吉林长春某双非普通一本,本科学的是人工智能-机器人工程专业,由于入学时刚开这个学院很多课开的不合理,计算机类的课只学了《数据结构》和《C语言》(c语言还是大一学的)。我数学基础还行,英语基础极差(六级考了好多次),在考研当年(上半年)才427勉强过六级(英语差的同学不用担心)。为什么选择哈工大?选学校之前我看过很多学校的信息,觉得22年考哈工大是比较有性价比的(信息收集真的很重要,考得好不如报的好)1.刚改专业课没几年,真题少(不卷)2.csapp很厚且没配套的哈工大网课(能吓退一堆人,其实不难学)_854计算机基础难么

Oracle中由 case when 报错 ORA-12704:字符集不匹配的简易解决_case when ora-12704-程序员宅基地

文章浏览阅读9.8k次,点赞2次,收藏4次。长话短说。今天涉及从db2转库到oracle的时候,测试系统发现sql语句中case t.CUR_UNIT when '万元' then 10000 when '亿元' then 100000000 end报错。错误提示ORA-12704:字符集不匹配。后查找原因发现涉及数据库中字段CUR_UNIT为nvarchar2类型,导致进行对比的字段类型不符。网上的解决方式一般是类型转_case when ora-12704

js数组排序的方法-程序员宅基地

文章浏览阅读690次,点赞20次,收藏5次。/ 按照年龄升序排序});// 输出:// [// ]这些都是在JavaScript中使用数组sort()方法进行排序的常见方法。注意,sort()方法会直接修改原数组,并返回修改后的数组。如果你不希望修改原数组,你需要先复制一份原数组再进行排序。

【k8s系列】使用MicroK8s 5分钟搭建k8s集群含踩坑经验-程序员宅基地

文章浏览阅读2.5k次,点赞2次,收藏7次。MicroK8s 是一个经过 CNCF 认证的轻量级的 Kubernetes开源部署工具,用于自动化部署、扩展和管理容器化应用程序。它在一个小的占用空间中提供了核心 Kubernetes 组件的功能,使其可以从单个节点扩展到高可用性生产集群。MicroK8s 是一个单一的软件包,使开发人员能够在60秒内获得功能齐全、一致且安全的 Kubernetes 系统。1.查看MicroK8s安装状态2.启动和停止MicroK8s服务3.查看集群节点和服务状态4.修改MicroK8s指令别名5.部署和运行APP。

如何用ESP8266 向手机App 发送信息_esp8266发送短信-程序员宅基地

文章浏览阅读8.8k次,点赞2次,收藏29次。首先是让ESP8266 开启热点,大概AT 指令如下:AT+CWMODE=2 //配置为AP 模式AT+CWSAP="name","password",1,3AT+CIPMUX=1 //启动多连接模式AT+CIPSERVER=1,8080 //配置为服务器模式并开启8080端口AT+CIPSTO=5000 //等待时间为5000s然后手机连接该热点,关于手机Ap..._esp8266发送短信

推荐文章

热门文章

相关标签