每天一道动态规划之第四天

news2024/12/23 4:31:03

一、与字符串相关的动态规划

1.1题目

给定一个字符串str,给定一个字符串类型的数组arr,出现的字符都是小写英文arr每一个字符串,代表一张贴纸,你可以把单个字符剪开使用,目的是拼出str来返回需要至少多少张贴纸可以完成这个任务例子: str= “babac,arr = [ba”,“c”“abcd”]把每一个字符单独剪开,含至少需要两张贴纸"ba"和"abcd”,因为使用这两张贴纸,有2个a、2个b、1个c
在这里插入图片描述
先来看一下这个题目吧!
其实用自然智慧来解这道题时,我的脑子直接给出了比较简单的答案。
最少肯定是一张贴纸,但一张贴纸无论是ba c 还是abcd都满足不了;
然后试一试两张呢?ba+ba=baba还差一个c就可以了。
那1张不行,2 张可以,那最少就是两张了。

这种方法可以解决这个最简单的问题,同时也从这个方法中找到一个思路。
没有解决思路的时候,暴力递归/暴力枚举是最常使用的方法。

我用第一张贴纸字符串str2去拟合字符串str中的数据,得到了一个字符串str=str-str1。(这里的减法就是str1分别去掉str2中的字符。)

如果str不为空,还要继续用贴纸去拟合

然后用第二张贴纸字符串str去拟合str,得到了一个字符串str=str-str1。(这里的减法就是str1分别去掉str2中的字符。)

如果str不为空,还要继续用贴纸去拟合…

这不就是有重复调用的函数,那不就是递归了吗?而递归结束的条件就是字符串为空str=“”;

所以面对一个str,我们现在有一个贴纸数组arr,我们会有的第一步就是用一张贴纸去拟合str,但这张贴纸可以是arr数组中的每一张贴纸。

在这里插入图片描述
上图只做了最左边的分支的示例,每个结点都应该继续往下,这很明显就是一颗树状图哈哈。

从最后一层向上看,当str=“c”时,如果用“ba”去拟合,那你无论怎么努力都拟合不了,那就是无效的。如果我们把每一方框都看成一个节点,每个节点都有一个属性叫最小贴纸数min_num。

那么“”的min_num为0—向上一层经过一个边,那就用了一张贴纸,所以要加一。那么上一层的min_num=0+1;

则普遍的公式就是min_num(上层)=min_num(下层)+1;

1.2代码

有上面的思路走下来肯定是一个暴力递归,但是没关系,任何东西都是又暴力开始(和平大概也是,但希望不是~~~~(>_<)~~~~),那我们就来尝试写一下代码吧!

public class solu01 {

    static String str = "baback";
    static String[] sticker = {"ba", "ck", "abcd"};

    public static void main(String[] args) {

        ways1();

    }


    public static void ways1() {
        int min_num = process1(str, sticker);
        min_num=min_num==Integer.MAX_VALUE?0:min_num;
        System.out.println(min_num);

    }


    public static int process1(String target, String[] sticker) {

        if (target.length()==0) {
            return 0;
        }

        int min_num = Integer.MAX_VALUE;
        for (String s : sticker) {
            String str = minus(target, s);
            int min_num_next = Integer.MAX_VALUE;
            ;
            if (!str.equals(target)) {
                min_num_next = process1(str, sticker);
            }
            if (min_num_next != Integer.MAX_VALUE) {
                min_num = Math.min(min_num, min_num_next + 1);
            }

        }
        return min_num;

    }

    public static String minus(String str1, String str2) {

        //可以看到这种贴纸类型的,可以将字符串剪开,其实无所谓顺序如何的,因此可以直接按照abc...这样的顺序来拍一下序
        //有一种很巧妙的办法就是
        int[] count = new int[26];//表示26个字母每一个出现的次数;
        char[] chars1 = str1.toCharArray();//target
        char[] chars2 = str2.toCharArray();

        for (char c : chars1) {
            count[c - 'a']++;
        }
        for (char c : chars2) {
            count[c - 'a']--;
        }


        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < count.length; i++) {
            if (count[i] != 0) {
                for (int j = count[i]; j > 0; j--) {
                    sb.append((char) (i + 'a'));
                }
            }

        }
        return sb.toString();
    }
}

1.3 代码优化

暴力递归之后该是什么呢?一般是用缓存去优化,但这里是一个与之前构建dp表不同的一个优化,他优化的点有两个一个是词频表的建立,一个是剪枝。

1)剪枝

先来说剪枝:
记不记得我们用一张贴纸先去拟合str,但我们这张贴纸就是按放在arr中的顺序都试一遍,没有什么策略。要说策略就是:按顺序都试一遍(很明显这是一句废话。)

但是有些你一眼看过去可能就觉得没必要啊。但这个一眼看过去转换成程序可以理解的描述,就是使用某种策略,直接排除某些选择,但又不影响结果。

比如说str=“babac”,那么我们就可以先去用含有‘b’的贴纸去拟合。

你说没‘b’的贴纸,可能有‘a’啊。

那我用‘b’拟合了,剩下的开头不就是‘a’吗,那还轮不到有‘b’没‘a’的贴纸吗?其实是不影响最后结果的。那么就没有必要把不含‘b’的贴纸放到我们所谓的*“第一张”*。

2)词频表

再来看看词频表,先说词频数组,词频数组是一个长度为26的数组,对于一个单词,统计每个字符输出的长度。比如“abc”的词频表如下图所示。
在这里插入图片描述
那如果我们建立贴纸数组的词频表数组{“ba”,“c”,“abcd”},其词频表:就是
在这里插入图片描述
在第一部分的代码里面,我们每次都要建一次贴纸的词频表和target的词频数组,那么干脆就直接建好了开始用就行。

所以用“剪枝”和“词频表”优化之后的代码为:

public class solu02 {
    static String str = "bababaq";
    static String[] sticker = {"ba", "ck", "abcd"};

    public static void main(String[] args) {


        ways2();

    }



    public static void ways2() {

        //建立词频表
        int[][] sticker_count=new int[sticker.length][26];
        for (int i=0;i<sticker.length;i++) {
            char[] chars = sticker[i].toCharArray();
            for (char aChar : chars) {
                sticker_count[i][aChar-'a']++;

            }
        }

        int min_num = process2(str, sticker_count);
        min_num=min_num== Integer.MAX_VALUE?0:1;
        System.out.println(min_num);

    }

    public static int process2(String s, int[][] sticker_count) {


        if (s.length() == 0) {
            return 0;
        }
        //0.设置要返回的变量
        char[] target=s.toCharArray();
        int min_num = Integer.MAX_VALUE;


        //1.建立target词频数组
        int[] target_count = new int[26];
        for (char c : target) {
            target_count[c - 'a']++;
        }
        //1.遍历贴纸词频表,找到所有分支种的最小值min_num
        int N=sticker_count.length;
        for (int i = 0; i < N; i++) {
            int[] sticker = sticker_count[i];
            //1.1如果这个贴纸包含target的第一个字符,我们可以选用这个贴纸作为第一张。
            if (sticker[target[0] - 'a'] > 0) {
                StringBuilder sb = new StringBuilder();
                //1.2把目标字符串中中的贴纸减掉
                for (int j = 0; j < 26; j++) {
                    if (target_count[j]>0) {
                        int num = target_count[j] - sticker[j];
                        for(int p=0;p<num;p++){
                            sb.append((char)(j+'a'));
                        }

                    }
                }
                //1.3恢复target
                String rest = sb.toString();
                min_num = Math.min(min_num, process2(rest, sticker_count));
            }

        }

        //2.如果返回的是有效值,那么返回的是min_num+1;
        return min_num+(min_num==Integer.MAX_VALUE?0:1);


    }
}

1.4 优化进阶

这里我们可能要想到用缓存法来进行优化,但之前学习的缓存大多都是数组。这个里面一直变换的是谁?是target的字符串,这个字符串的状态如果都列出来占用空间会非常大,因此需要可以说没有严格的位置依赖。没有严格位置依赖但是依赖状态的,或者说状态无法穷举的,缓存就用hashmap来进行。

public class solu03 {
    static String str = "bababakq";
    static String[] sticker = {"ba", "ck", "abcd"};

    public static void main(String[] args) {


        ways3();

    }

    public static void ways3() {

        //
        HashMap<String,Integer> dp=new HashMap<>();

        //建立词频表
        int[][] sticker_count=new int[sticker.length][26];
        for (int i=0;i<sticker.length;i++) {
            char[] chars = sticker[i].toCharArray();
            for (char aChar : chars) {
                sticker_count[i][aChar-'a']++;

            }
        }

        int min_num = process3(str, sticker_count,dp);
        min_num=min_num== Integer.MAX_VALUE?0:min_num;
        System.out.println(min_num);



    }

    public static int process3(String s, int[][] sticker_count, HashMap<String , Integer> dp) {


        if(dp.containsKey(s)){
            return dp.get(s);
        }
        if (s.length() == 0) {
            return 0;
        }
        //0.设置要返回的变量
        char[] target=s.toCharArray();
        int min_num = Integer.MAX_VALUE;


        //1.建立target词频数组
        int[] target_count = new int[26];
        for (char c : target) {
            target_count[c - 'a']++;
        }
        //1.遍历贴纸词频表,找到所有分支种的最小值min_num
        int N=sticker_count.length;
        for (int i = 0; i < N; i++) {
            int[] sticker = sticker_count[i];
            //1.1如果这个贴纸包含target的第一个字符,我们可以选用这个贴纸作为第一张。
            if (sticker[target[0] - 'a'] > 0) {
                StringBuilder sb = new StringBuilder();
                //1.2把目标字符串中中的贴纸减掉
                for (int j = 0; j < 26; j++) {
                    if (target_count[j]>0) {
                        int num = target_count[j] - sticker[j];
                        for(int p=0;p<num;p++){
                            sb.append((char)(j+'a'));
                        }

                    }
                }
                //1.3恢复target
                String rest = sb.toString();
                min_num = Math.min(min_num, process3(rest, sticker_count,dp));
            }

        }


              //2.如果返回的是有效值,那么返回的是min_num+1;
        min_num=min_num+(min_num==Integer.MAX_VALUE?0:1);
        dp.put(s,min_num);

        return min_num;


    }


}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/964613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue第1篇前端的发展历史

一 vue的发展历史 1.1 vue vue是一个渐进式的javascript框架&#xff0c;Vue 的核心库只关注视图层&#xff0c;方便与第三方库或既有项目整合。 1.2 前端知识核心快捷汇总 Soc原则&#xff1a;关注点分离原则 HTML CSS JS : 视图 &#xff1a; 刷新后端提供的数据展示…

经管博士科研基础【16】一元二次函数的解的公式

1. 一元二次函数的形式 2. 一元二次函数的图形与性质 一元二次函数的图像是一条抛物线&#xff0c;图像定点公式为(-b/2a,4ac-b*b/4a)&#xff0c;对称轴位直线x-b/2a。 3. 求根公式 形如ax*xb*xc0的一元二次方程&#xff0c;其求根公式为&#xff1a; 4. 韦达定理 如果x1和…

财报解读:份额企稳、均价上浮,小米高端化驶入正轨?

时隔半个月&#xff0c;在雷军那场颇有反响的以“成长”为主题的个人演讲之后&#xff0c;小米发布了今年二季度财报。二季报中亮眼的财务数据&#xff0c;为雷军的演讲提供了一份更加有力的注解&#xff0c;小米经过三年探索&#xff0c;似乎已经找到了高端化的诀窍&#xff0…

C++:日期类

学习目标&#xff1a; 加深对四个默认构造函数的理解&#xff1a; 1.构造函数 2.析构函数 3.拷贝构造 4.运算符重载 实现功能 1.比较日期的大小 2.日期-天数 3.前/后置&#xff0c;-- 这里基本会使用运算符重载 定义一个日期类 class Date { public://1.全缺省参数的构造函数Da…

Ubuntu的C++环境配置

文章目录 1. 如果没有配置C环境会有情况出现2. Ubuntu的C环境配置方法 上篇介绍了Ubuntu中安装和配置QtCreator&#xff0c;下来将会介绍C的相关内容。 C的简介此处不做介绍。 想要在Ubuntu上运行C程序&#xff0c;首先需要先配置好C环境。 1. 如果没有配置C环境会有情况出现…

WEB项目利用Eclipse打包成war包并部署在CentOS8

1、Eclipse把WEB项目打包成war包 2、Xftp上传war包到Linux中安装Tomcat的webapps目录 /usr/local/tomcat/apache-tomcat-9.0.80/webapps3、利用IP地址访问部署的项目 在CentOS中可用 ifconfig找到对应的IP地址 http://192.168.122.2:8080/CentOS-Web/index.html

基于SSM的物资管理系统的设计与实现(论文+源码)_kaic

【摘 要】此物资管理系统是针对生产型企业开发的。通过分析企业的实际管理工作中存在的问题&#xff0c;与管理系统开发的详细步骤及系统开发的原理&#xff0c;并利用计算机的运算速度快、存储容量大、处理逻辑问题强等优点&#xff0c;将管理工作与计算机结合起来。根据管理工…

【文心一言】学习笔记

学习资料 《听说文心一言App霸榜了&#xff0c;那必须来一波全方位实测了》 情感陪伴&#xff1a;文心一言 App 可以充当用户的情感树洞&#xff0c;提供知心姐姐、【暖男】等角色扮演&#xff0c;为用户提供情绪疏导、情感分析、约会建议等服务。 1. 模型属性 【提示词工具…

leetcode:1710. 卡车上的最大单元数(python3解法)

难度&#xff1a;简单 请你将一些箱子装在 一辆卡车 上。给你一个二维数组 boxTypes &#xff0c;其中 boxTypes[i] [numberOfBoxesi, numberOfUnitsPerBoxi] &#xff1a; numberOfBoxesi 是类型 i 的箱子的数量。numberOfUnitsPerBoxi 是类型 i 每个箱子可以装载的单元数量。…

C++算法 —— 分治(2)归并

文章目录 1、排序数组2、数组中的逆序对3、计算右侧小于当前元素的个数4、翻转对 1、排序数组 排序数组 排序数组也可以用归并排序来做。 vector<int> tmp;//写成全局是因为如果在每一次小的排序中都创建一次&#xff0c;更消耗时间和空间&#xff0c;设置成全局的就更高…

ROS-4.创建发布者和订阅者

ros中非长连接的通信使用topic的方式&#xff0c;publisher向topic发布消息&#xff0c;subscriber订阅topic消息&#xff0c;对于非应答模式的通信适合使用该模式&#xff0c;如下图 接下来我们实现一个发布者和订阅者 1. 创建功能包 在实现订阅者和发布者的时候我们需要先…

oled--SSD1315驱动

OLED 接口方式&#xff08;由硬件电路确定&#xff09;&#xff1a;6800、8080、spi、i2c. 常见的驱动芯片&#xff1a;ssd1306、ssd1315。 oled屏幕的发光原理不同于lcd&#xff0c;上电后无法直接显示&#xff0c;需要初始化后才能正常显示。 SSD1315手册资料 SSD1315是一款…

微信小程序手机号快速验证组件调用方式

目录 一、测试环境 二、问题现象 三、总结 手机号验证组件&#xff08;包括快速验证组件和实时验证组件&#xff09;调用后无法对事件进行回调这个问题&#xff0c;先说结论&#xff0c;以下是正确的使用方式&#xff1a; <!-- 手机号快速验证组件 --> <button op…

第三章 图像到图像的映射

文章目录 第三章 图像到图像的映射3.1 单应性变换3.1.1 直接线性变换算法3.1.2 仿射变换 3.2 图像扭曲3.2.1 图像中的图像3.2.2 分段仿射扭曲3.2.3 图像配准 3.3 创建全景图3.3.1 RANSAC3.3.2 稳健的单应性矩阵估计3.3.3 拼接图像 第三章 图像到图像的映射 本章讲解…

Pandas数据分析基础—pandas自带函数map()/apply()/applymap()

文章目录 前言一、Series数据处理1、map()方法2、apply()方法3、applymap()方法总结 二、DataFrame数据处理1、map()方法2、apply()方法3、applymap()方法总结 三、map、apply、applymap三个函数区别 前言 在进行数据处理时&#xff0c;经常会对一个DataFrame展开逐行、逐列、…

three.js(二):webpack + three.js + ts

用webpackts 开发 three.js 项目 webpack 依旧是主流的模块打包工具;ts和three.js 是绝配&#xff0c;three.js本身就是用ts写的&#xff0c;ts可以为three 项目提前做好规则约束&#xff0c;使项目的开发更加顺畅。 1.创建一个目录&#xff0c;初始化 npm mkdir demo cd de…

【项目 计网8】4.23 TCP状态转换 4.24半关闭、端口复用

文章目录 4.23 TCP状态转换关于三次握手四次挥手 4.24半关闭、端口复用端口复用 4.23 TCP状态转换 2MSL(Maximum Segment Lifetime) 主动断开连接的一方&#xff0c;最后进入一个TIME_WAIT状态&#xff0c;这个状态会持续&#xff1a;2msl msl&#xff1a;官方建议&#xff1a;…

Linux 多进程解决客户端与服务器端通信

写一个服务器端用多进程处理并发&#xff0c;使两个以上的客户端可以同时连接服务器端得到响应。每当接受一个新的连接就fork产生一个子进程&#xff0c;让子进程去处理这个连接&#xff0c;父进程只用来接受连接。 与多线程相比的不同点&#xff1a;多线程如果其中一个线程操…

Kubernetes(K8s 1.28.x)部署---创建方式Docker(超详细)

目录 一、基础环境配置&#xff08;所有主机均要配置&#xff09; 1、配置IP地址和主机名、hosts解析 2、关闭防火墙、禁用SELinux 3、安装常用软件 4、配置时间同步 5、禁用Swap分区 6、修改linux的内核参数 7、配置ipvs功能 二、容器环境操作 1、定制软件源 2、安…

RocketMQ消息队列-@RocketMQMessageListener实现原理

使用Spring-RocketMQ时&#xff0c;只需要引入rocketmq-spring-boot-starter包&#xff0c;并且定义以下消费者&#xff0c;就可以很简单的实现消息消费 Component RocketMQMessageListener(topic "first-topic", consumerGroup "my-producer-group", s…