iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java花式解决'分割回文串ii'问题详解
  • 326
分享到

Java花式解决'分割回文串ii'问题详解

2024-04-02 19:04:59 326人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录前言题目思路分析案例说明初级代码代码升级1.回文串动归2.综合动归3.奇思妙想前言 最学习动态规划思想的路上,遇见了‘分割回文串问题',如临大敌啊,题目听起来蛮简单,思考起来却也

前言

学习动态规划思想的路上,遇见了‘分割回文串问题',如临大敌啊,题目听起来蛮简单,思考起来却也没那么容易,比解决问题更头疼的是如何将解决方法进行优化,使得时间空间复杂度尽量的小,经过了反复的挣扎思考,终于总结出来了这一篇 分割回文串 ii 的文章,花式解决该问题,总有一款适合你。

牛客链接

题目

给出一个字符串s,分割s使得分割出的每一个子串都是回文串

计算将字符串s分割成回文分割结果的最小切割数

例如: 给定字符串s=“aab”,

返回1,因为回文分割结果[“aa”,“b”]是切割一次生成的。

思路分析

首先,已知字符串s的长度为len,想要求前len个字符串被切割成回文串所需要的最小切割数,就会很自然的想到求前i个字符形成的字符串切割成回文串需要的最小切割数,即为状态F(i)。

然后,会想到前i个字符形成的字符串分割变成回文串需要的最大切割数为i-1。例如字符串"aab",切2刀形成长度为1的回文串"a",“a”,“b”。

再然后,关键在于求解最小切割数的过程,这里采取暴力求解,定义变量j,使之小于i,在我们已知状态F(j)的情况下(即前j个字符形成的字符串的最小切割次数),如果[j+1,i]是回文串,那么再来上一刀就可以求出当前用最少的切割次数。那么此时F(i)= min(F(i),F(j)+1),意思就是在上一次求得的前i个字符串的分割次数和这一次求得的次数进行对比,取最小值。(注:这里的[j+1,i]指的是字符串第j+1个字符到第i个字符的意思,并非字符串下标索引,写代码时,转换成索引就应该是求下标为j-1的字符到下标为i的字符形成的字符串是否为回文串)

紧接着,就该实现判断是否为回文串的方法,简单的思想就是,为该方法提供字符串s,提供子串的起始下标start与终点下标end,start<end的条件下,使start向后走,end向前走,但凡对应到字符串s中的字符不一样,就说明不是回文串,返回false,如果成功遍历完了循环,说明是回文串,返回true。

最后,将F(len)的值返回。

动归四角度:

1.状态定义F(i):字符串s的前i个字符的最小分割次数;

2.状态间的转移方程定义:F(i)=min(F(i),F(j)+1),0 <= j < i,且F(j)为已知状态,当[j+1,i]为回文串时,执行此状态转移方程

3.状态的初始化:F(i) = i-1,注意F(0)为-1;

例如,当字符串为"aa",因为[1,2]为回文串,F(2)= min(F(2),F(0)+1)=min(1,0)= 0,得到正确答案

4.返回结果 :F(s.length());

案例说明

为了方便理解,这里采取了更长的字符串"aabaa",一步步带你走过程。

初级代码


import java.util.*;
public class Solution {
    //判断是否为回文串
    public boolean func(String s,int start,int end) {
        while(start<end) {
            if(s.charAt(start)!=s.charAt(end)) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
    public int minCut (String s) {
        int len = s.length();//字符串的长度
        if(len == 0) return 0;//当长度为0时直接返回0
        int[] count = new int[len + 1];//用于记录状态
        //状态初始化
        for(int i = 0;i <= len;i ++) {
            count[i] = i-1;
        }
        for(int i = 1;i <= len;i++) {
            for(int j = 0;j <= i-1;j++) {
                if(func(s,j,i-1)) {
                    count[i] = Math.min(count[i],count[j]+1);//状态转移方程
                }
            }
        }
        return count[len];//返回结果
    }
}

在整个进行状态计算的过程中,两层for循环时间复杂度为O(N2),判断是否为回文串的方法时间复杂度为O(N),因此总的来说,总的时间复杂度为O(N3)

代码升级

可以看出来,用上面的代码时间复杂度还是比较高的,因此代码还需升级才是

1.回文串动归

首先,关于回文串的判断方法,每次判断是否要进行状态转移方程时都要调用回文串方法,这真的有必要吗,或许也可以使用动态规划的思想将每种字符子串是否为回文串的状态记录下来。

状态四角度:

1.状态定义F(i,j):字符区间[i,j]是否为回文串

2.状态间的转移方程定义F(i,j):

如果i == j,表示单字符,F(i,j) = true;

如果j == i+1,表明俩字符是紧挨着的,如果在总字符串s中对应的字符相同,F(i,j)= true,反之F(i,j) = false;

其他的情况中,F(i,j) = (s.charAt(i) == s.charAt(j)) && F(i+1,j-1);

该转移方程的意思为字符首尾字符相同,且去掉字符区间的首位字符后的字符区间的状态F(i+1,j-1)仍然为回文串才证明[i,j]字符串区间为回文串即F(i,j)= true

3.状态的初始化:F(i,j) = false

4.返回结果状态二维布尔类型数组

注:由于在状态转移的过程中,求F(i,j)会只用到之前已经计算过的状态F(i+1,j-1),这就意味着i需要从后向前遍历,使用的是已经更新过结果的值


import java.util.*;
public class Solution {
    //判断是否为回文串
    public boolean[][] func2 (String s) {
        int len = s.length();//字符串的长度
        boolean[][] ret = new boolean[len][len];
        //记录状态的二维数组,默认值为false
        //由于i<=j<len,所以ret数组实际只更新了一半
        for(int i = len;i >= 0;i--) {
            for(int j = i;j<len;j++) {
                if(i == j) {
                    ret[i][j] = true; //单字符比为回文串
                }else if(j == i+1) {
                    if(s.charAt(i) == s.charAt(j)) {
                        ret[i][j] = true; //相邻字符相同为回文串
                    }else{
                        ret[i][j] = false;//相邻字符不同就不是回文串
                    }
                }else{
                    ret[i][j] = (s.charAt(i) == s.charAt(j)) && ret[i+1][j-1];
                    //其余转移情况
                }
            }
        }
        return ret;//返回结果
    }
    public int minCut (String s) {
        int len = s.length();
        if(len == 0) return 0;
        int[] count = new int[len + 1];
        //状态初始化
        for(int i = 0;i <= len;i ++) {
            count[i] = i-1;
        }
        boolean[][] ret = func2(s);//调用判断回文串方法,获得所有字符子串的是否为回文串的情况
        for(int i = 1;i <= len;i++) {
            for(int j = 0;j <= i-1;j++) {
                //直接在ret数组中找结果,避免反复调用回文串判断方法
                if(ret[j][i-1]) {
                    count[i] = Math.min(count[i],count[j]+1);//状态转移方程
                }
            }
        }
        return count[len];//返回结果
    }
}

在该方法中,判断回文串的方法时间复杂度为O(N2),但因为在主方法中只调用了一次,且回文串判断方法中只更新了一般的值,因此总的时间复杂度为O(N2)~O(2*N2)

2.综合动归

可以看的出来上面的代码还是比较长的,回文串判断方法用到了两层循环,主方法也用到了两层循环,这不也是优化的方向蛮,或许可以把它们放在同一个两层循环中。

注:由于回文串判断方法中的i是一定要从后向前遍历的,因此主函数的初识值就需要调整为count[i] = len - i - 1,返回的结果为F(0)


import java.util.*;
public class Solution {
    public int minCut(String s) {
        int len = s.length();//字符串的长度
        if(len == 0) return 0;
        int []count = new int[len+1]; //存放最小分割次数状态的数组
        boolean [][]p = new boolean[len][len];//存放[i,j]字符区间是否为回文串的二维数组
        for(int i = 0; i <= len; i++) count[i] = len - i - 1;//状态初始化
        for(int i = len-1;i >= 0;i--){
            for(int j = i;j < len;j++){
                //j-i<2 条件成立且第一个条件成立包含着单个字符串和相邻字符串的情况
                //p[i+1][j-1] 为 ture 且第一个条件成立则代表着其他的回文串状态转移类型
                //以上情况有一项成立则F(i,j)为 ture
                if(s.charAt(i) == s.charAt(j) && (j-i<2||p[i+1][j-1])){
                    p[i][j] = true;
                    count[i] = Math.min(count[i],count[j+1]+1);//状态转移方程
                }
            }
        }
        return count[0];//返回结果
    }
}

通过这样的方法,直接将时间复杂度降到了O(N2)

3.奇思妙想

上面几种方法,需要将回文串的判断状态都记录下来,且判断回文串的方法都是从子字符串的两头向中间进行判断,或许有一种方法,可以直接不用记录下来每种子字符串的是否为回文串的状态,并且从中间向两头进行判断回文串。

可以设置两个变量i和j,[i,j]且j==i代表着下标为i的单个字符,必定是回文串,F(j+1)= min(F(j+1),F(i)+1),以此为中心,i--,j++,如果区间两头的字符相同,说明[i-1,j+1]的区间字符串为回文串,在不超出原字符串s的总区间[0,len-1]的循环情况下,重复上面的操作,直到循环条件不成立

回文串可能是奇数个字符,也可能是偶数个字符,上面的情况是奇数个字符的情况,换成偶数个字符的情况只需要判断[i,i+1]是否为回文串,如果是,就参考上面的方式,以此为中心向两头展开,求以[i,i+1]为中心最长的回文串,从而求出每个状态的最小分割数。

案例说明:


import java.util.*;
public class Solution {
    public int minCut(String s) {
        int len = s.length();//字符串的长度
        if(len == 0) return 0;
        int[] count = new int[len + 1];
        for(int i = 0; i <= len; i++) count[i] = i - 1;//状态初始化
        for(int i = 0; i < len; i++) {
            func3(s, i, i, count);//奇数个字符的回文串
            func3(s, i, i + 1, count);//偶数个字符的回文串
        }
        return count[len];//返回结果
    }     
    private  void func3(String s, int i, int j, int[] count) {
        //不超过字符串s的区间范围且下标i的字符和下标j的字符相等的条件下向两头扩展,得到最长的回文串,以此来求出状态
        while(i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) {
            count[j + 1] = Math.min(count[j + 1], count[i] + 1); //状态转移
            --i;//左区间扩展一格
            ++j;//右区间扩展一格
        }
        return;
    }  
} 

到此这篇关于Java花式解决'分割回文串 ii'问题详解的文章就介绍到这了,更多相关Java解决分割回文串 ii内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java花式解决'分割回文串ii'问题详解

本文链接: https://www.lsjlt.com/news/160513.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java花式解决'分割回文串ii'问题详解
    目录前言题目思路分析案例说明初级代码代码升级1.回文串动归2.综合动归3.奇思妙想前言 最学习动态规划思想的路上,遇见了‘分割回文串问题',如临大敌啊,题目听起来蛮简单,思考起来却也...
    99+
    2024-04-02
  • Java怎么解决分割回文串问题
    本篇内容主要讲解“Java怎么解决分割回文串问题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java怎么解决分割回文串问题”吧!题目给出一个字符串s,分割s使得分割出的每一个子串都是回文串计算...
    99+
    2023-06-22
  • Java回溯法解决全排列问题流程详解
    题目描述: 给定一不重复的数组,返回其具有的所有全排列(使用 List<List > 返回) 思路: 以数组 nums = [1, 2, 3] 为例,其具有的解空间可以用...
    99+
    2024-04-02
  • Java跨域问题分析与解决方法详解
    目录一、前言二、什么是跨域问题三、 为什么会出现跨域问题四、什么情况下会出现跨域五、如何解决跨域问题5.1 使用@CrossOrigin注解5.2 使用WebMvcConfigure...
    99+
    2023-05-20
    Java跨域问题原理 Java跨域问题解决方法 Java跨域问题
  • 详解Java分布式缓存系统中必须解决的四大问题
    目录缓存穿透缓存击穿缓存雪崩缓存一致性分布式缓存系统是三高架构中不可或缺的部分,极大地提高了整个项目的并发量、响应速度,但它也带来了新的需要解决的问题,分别是: 缓存穿透、缓存击穿、...
    99+
    2024-04-02
  • C++回溯与分支限界算法分别解决背包问题详解
    目录算法思想回溯代码分支限界代码算法思想 分支限界法与回溯法的求解目标不同。 回溯法的求解目标是找出解空间中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,...
    99+
    2024-04-02
  • Java Spring Boot分布式事务问题怎么解决
    这篇文章主要讲解了“Java Spring Boot分布式事务问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java Spring Boo...
    99+
    2023-07-02
  • java返回json请求中文变成问号的问题及解决
    目录java返回json请求中文变成问号json返回中文全是问号java返回json请求中文变成问号 原来在个人项目时,用layui的数据表格获取数据时,不会出现中文变问号问题 后来...
    99+
    2024-04-02
  • 详解Java分布式系统中session一致性问题
    业务场景 在单机系统中,用户登陆之后,服务端会保存用户的会话信息,只要用户不退出重新登陆,在一段时间内用户可以一直访问该网站,无需重复登陆。用户的信息存在服务端的 session 中...
    99+
    2024-04-02
  • 详解SpringCache使用Redisson分布式锁解决缓存击穿问题
    目录1 什么是缓存击穿2 为什么要使用分布式锁3 什么是Redisson4 Spring Boot集成Redisson4.1 添加maven依赖4.2 配置yml4.3 配置Redi...
    99+
    2024-04-02
  • 解决Java中文乱码问题:使用System.out.println输出中文字符串
    解决Java中文乱码问题:使用System.out.println输出中文字符串 在Java编程中,当我们想要在控制台输出中文字符串时,有时会遇到乱码的问题。本文将介绍如何解决这个问题,并提供相应的源...
    99+
    2023-10-25
    java python 开发语言 Java
  • java暴力匹配及KMP算法解决字符串匹配问题示例详解
    目录要解决的问题?一、暴力匹配算法一个图例介绍KMP算法二、KMP算法算法介绍一个图例介绍KMP算法  代码实现要解决的问题? 一、暴力匹配算法 一个图例介绍KMP算法 St...
    99+
    2024-04-02
  • Java分布式系统中session一致性问题怎么解决
    小编给大家分享一下Java分布式系统中session一致性问题怎么解决,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Java可以用来干什么Java主要应用于:1....
    99+
    2023-06-14
  • java只返回实体类中的部分字段问题如何解决
    这篇文章主要介绍了只返回实体类中的部分字段问题如何解决,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。如何只返回实体类中的部分字段在实体类上添加注解@JsonInclude(JsonInclude.Include.NON_...
    99+
    2023-07-06
  • JAVA中文比较问题的分析和解决是怎样的
    这篇文章将为大家详细讲解有关JAVA中文比较问题的分析和解决是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。 Java的中文问题由来已久,前不久需要做内存中的中文比较排序,对字符串进行...
    99+
    2023-06-03
  • 用Java编写分布式系统:常见问题和解决方案。
    分布式系统是现代计算机技术的一项重要应用,它可以将一个大型系统分割成多个相互独立的子系统,这些子系统可以在不同的物理机器上运行,从而提高系统的性能、可扩展性和可靠性。而Java作为一种跨平台、面向对象、高性能的编程语言,也成为了开发分布式系...
    99+
    2023-09-24
    leetcode path 分布式
  • 如何解决生产环境分布式文件系统崩了问题
    这篇文章主要讲解了“如何解决生产环境分布式文件系统崩了问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何解决生产环境分布式文件系统崩了问题”吧!问题定位...
    99+
    2024-04-02
  • 一文教你迅速解决分布式事务XA一致性问题
    近日,腾讯云发布了分布式数据库解决方案(DCDB),其最明显的特性之一就是提供了高于开源分布式事务XA的性能。大型业务系统有着用户多、并发高的特点,在这方面,集中式数据库(单机数据库)的性能很难支持,因此主流的互联网公司往往采用分布式(架构...
    99+
    2023-06-04
  • Java分布式缓存系统中必须解决的四大问题是什么
    这篇文章主要介绍了Java分布式缓存系统中必须解决的四大问题是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java分布式缓存系统中必须解决的四大问题是什么文章都会有所收获,下面我们一起来看看吧。分布式缓存...
    99+
    2023-06-30
  • 完美解决springboot项目出现”java:错误:无效的源发行版:17“问题(图文详解)
    springboot项目出现”java: 错误: 无效的源发行版:17“问题解决方案 下面是报错页面 问题解析 在我个人遇到此问题的情况下,出现此错误的原...
    99+
    2023-05-18
    springboot错误 无效的源发行版:17 springboot无效发行版:17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作