两个经典回文字符串问题中的巧妙算法

2023-11-07

问题一:(最长回文子串)给定一个字符串 s,找到 s 中最长的回文子串。
第一眼的想法是暴力法,由于其时间复杂度高达O(n^3),当s过长时效率会特别低。
方法一:中心扩展算法
其思想就是遍历一遍字符串,其中在每一个点都进行以其为中心而均匀展开(分奇偶),然后找到每个点能够展开到的最大值,最后也就不难得到最长回文串了。
具体代码如下:

public String longestPalindrome1(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            //分两种情况解决了奇偶的问题
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            //通过start和end来保存最大len的两端
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    private int expandAroundCenter(String s, int left, int right) {
        int L = left, R = right;
        while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
            L--;
            R++;
        }
        return R - L - 1;
    }

方法二:Manacher 算法
其思想就是将原字符串用同一的符号扩展两倍+1,这样可以巧妙的回避了奇偶回文串的问题。然后再定义一个类似计数排序的一个计数数组,用它在扩展后的字符串上进行类型中心扩展的操作。最终的Manacher 算法还有一个对计数数组的一个算法优化。
具体代码如下:

public String longestPalindrome2(String s) {
        char[] ch = s.toCharArray();
        char[] ah = new char[2 * s.length() + 1];//回避了要讨论奇偶两种情形的弊端
        for (int i = 0; i < 2 * s.length(); i = i + 2) {
            ah[i] = '#';
            ah[i + 1] = ch[i / 2];
        }
        ah[2 * s.length()] = '#';
        int[] arr = Manacher1(ah);
        return s.substring((arr[1] - arr[0]) / 2, (arr[1] + arr[0]) / 2);
    }

    private int[] Manacher1(char[] t) {
        int[] arr = new int[2];
        int[] len = new int[t.length];//既用于计数还能参与遍历
        int ans = 0;
        for (int i = 0; i < len.length; i++) {
            len[i] = 1;
        }
        for (int i = 0; i < len.length; i++) {
            while ((i - len[i] >= 0) && (i + len[i] < len.length) && t[i - len[i]] == t[i + len[i]]) {
                len[i]++;
            }
            ans = Math.max(ans, len[i]);
        }
        arr[0] = ans - 1;
        int i = 0;
        while (len[i] != ans) {
            i++;
        }
        arr[1] = i;
        return arr;
    }
    private int[] Manacher2(char[] t) {
        int[] arr = new int[2];
        int[] len = new int[t.length];
        int ans = 0;
        //另外两个指针起到优化算法的作用
        int mx = 0;
        int po = 0;
        for (int i = 0; i < len.length; i++) {
            if (mx > i) {
                len[i] = Math.min(mx - i, len[2 * po - i]);//如果前一个位置所形成的的回文串长度很大,那么下一个位置必定继承其部分长度
            } else {
                len[i] = 1;
            }
            while ((i - len[i] >= 0) && (i + len[i] < len.length) && t[i - len[i]] == t[i + len[i]]) {
                len[i]++;
            }
            if (len[i] + i > mx) {
                mx = len[i] + i;
                po = i;
            }
            ans = Math.max(ans, len[i]);
        }
        arr[0] = ans - 1;
        int i = 0;
        while (len[i] != ans) {
            i++;
        }
        arr[1] = i;
        return arr;
    }

问题二:(最短回文串)给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
思路:由于只能在字符串的前面添加字符,所以不由的想出要先找到原字符串的最长前缀回文串,下面给出两种方法。
方法一:直接寻找
这里需要倒序遍历字符串,定义一头一尾两个指针,再用一个while的内循环来取得其最长的前缀回文串。
具体代码如下:

public String shortestPalindrome1(String s) {
        if (s.equals("") || s.length() == 1) {
            return s;
        }
        int x = getFrontS(s);
        return new StringBuilder(s.substring(x + 1)).reverse().toString() + s;
    }
    //获得原字符串的最长前缀回文字符串长度
    private int getFrontS(String s) {
        int a = 0;
        for (int i = s.length() - 1; i > 0; i--) {
            if (s.charAt(a) == s.charAt(i)) {
                int b = i - 1;
                a++;
                while (a < b) {
                    if (s.charAt(a) == s.charAt(b)) {
                        a++;
                        b--;
                    }
                    else {
                        break;
                    }
                }
                if (a >= b) {
                    return i;
                }
                a = 0;
            }
        }
        return 0;
    }

方法二:KMP算法的next数组
这个next数组也很类似计数排序的那个计数数组,只不过这个数组记录的是当前位置的最大前缀数,有了这个数组,最长前缀回文串也就手到擒来了。
具体代码如下:

public String shortestPalindrome2(String s) {
        if (s.equals("") || s.length() == 1) {
            return s;
        }
        String temp = s + "#" + new StringBuilder(s).reverse().toString() + "#";
        //中间那个#是为了把两段字符串分隔开,避免产生干扰
        int[] next = getNext1(temp);
        int index = next[temp.length()-1];  //取得最长前缀回文字符串的下标
        return new StringBuilder(s.substring(index)).reverse().toString() + s;
    }
    private int[] getNext1(String p) {
        int[] next = new int[p.length() + 1];
        next[0] = -1;
        for (int i = 0; i < p.length(); i++) {
            next[i + 1] = next[i] + 1;
            while (next[i + 1] > 0 && p.charAt(next[i + 1] - 1) != p.charAt(i)) {
                next[i + 1] = next[next[i + 1] - 1] + 1;
            }
        }
        return next;
    }

特别是后面的Manacher 算法和KMP算法都很巧妙也很有回味价值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

两个经典回文字符串问题中的巧妙算法 的相关文章

  • 2020年最新的PHP面试题(附答案)

    684 PHP究竟是不是最好的语言 一直以来是程序员最大的 争议 但毋庸置疑的是 PHP绝对是最有前途和力量的变成语言 也是你入门最值得学习的语言 为什么这么说呢 不妨来了解一下 为什么学PHP 语言入门简单 容易掌握 程序建设性好 开发者
  • javaWeb的项目路径问题(对servlet路径和form表单请求路径的一些归纳)

    javaWeb项目的路径问题 这篇文章大神将路径问题讲的很透彻 我想说的是几点小细节 博主说的很清楚 如果这里的deployment下面的application context中不单单仅是一个 后面加的有其他字符的话 在form表单中我们填
  • 深聊测开领域之:Testcase中资源泄露

    1 引言 2 何为资源泄露 2 1 资源泄露定义 2 2 TestCase 中资源泄露 3 避免资源泄露 3 1 如何避免资源泄露 3 2 自动化执行顺序 1 引言 执行测试时离不开测试用例 测试用例辅佐执行测试 这就好比皇帝与宰相 需要的
  • jekyll网站上传服务器,jekyll 高效搭建个人博客之完整流程

    jekyll png 原创精选来自我的博客文章 目录 说在前面的话 作为一个和电脑 代码打交道的我 一直都想拥有自己的博客 一切都显得那么高 zhuang 大 bi 上 yong 在下定决心之后就在网上到处查找资料 最终发现一般用的就三种
  • 定时任务之Springboot整合Quartz详解

    文章目录 一 什么是Quartz 二 为什么使用Quartz 1 为什么要用定时任务 2 为什么使用Quartz 三 常见开源定时任务的框架的异同 四 Quartz的组件 五 Quartz持久化 1 为什么要持久化 2 Quartz提供了两
  • 解决openwrt ipk missing dependencies libpthread librt

    新版本的trunk有在ipk打包的过程中的bug 他不能自动识别SDK中已经变异的动态链接库 比如libpthread libboost这些 解决方案是修改与pakage里同级的makefile的内容 可以修改如下 主要是添加DEPENDS
  • JavaScprit---基础代码

    var text Hello World document write p text indexOf Hello p document write p text indexOf World p document write p text i
  • MySQL中Select+Update并发的更新问题

    小知识补充 首先 我们要知道在mysql中update操作都是线程安全的 mysql引擎会update的行加上 排他锁 其他对该行的update操作需要等到第一个update操作提交成功或者回滚 才能获取这个 排他锁 从而对该行进行操作 例
  • 程序员如何进行职业规划?

    本文来自作者 王俊生 在 GitChat 上分享 程序员如何进行职业规划 阅读原文 查看交流实录 文末高能 编辑 哈比 一直以来程序员这一职业都给人高薪资的印象 近年来随着互联网行业的快速发展 程序员更是人满为患 然而很多人关注的却是程序员
  • MOS管电源开关电路的软启动

    https mp weixin qq com s 5W8rveh69XVzJoRX XrSfgbeizhu 仅用于标注 适用于我这样的硬件小白
  • React你应该学会的开发技巧【总结】!

    关注公众号 前端人 回复 加群 添加无广告优质学习群 干净的代码不仅仅是工作代码 简洁的代码易于阅读 易于理解并且井井有条 在本文中 我们将研究六种编写更简洁的React代码的方法 在阅读这些建议时 请务必记住它们的实质 相信这些实践对我们
  • 链表两数相和 (2)--Leetcode

    题目描述 给出两个 非空 的链表用来表示两个非负的整数 其中 它们各自的位数是按照 逆序 的方式存储的 并且它们的每个节点只能存储 一位 数字 如果 我们将这两个数相加起来 则会返回一个新的链表来表示它们的和 您可以假设除了数字 0 之外
  • 通过在线游戏练习flex布局和各种css选择器

    懒是一个很好的托辞 说的好像你勤奋了就能干成大事一样 序言 不知道身为后端程序员的你写不写前端代码 反正我是经常写 调布局和样式着实有点费劲 一般像我这样的后端开发都不会系统的去学一些前端知识 都是遇到了就百度 然后复制粘贴一下 再不停的让
  • MyBatis七:自定义映射resultMap

    自定义映射resultMap 一 resultMap处理字段和属性的映射关系 为字段设置别名 是别名和属性名一致 方式一
  • STM32 系统定时器--SysTick

    目录 一 结构图与寄存器 二 SysTick定时时间计算 三 Systick 系统定时器配置程序 如何更改systick中断优先级 四 实验设计 1 通过查询标志位来写延时函数 2 通过使能中断来写延时函数 SysTick 系统定时器 24
  • Java学习笔记之用Box布局swing界面

    主要就用到了这四个方法 createHorizontalBox 创建一个从左到右显示其组件的 Box createHorizontalStrut int width 左右部件之间的中间间隔就可以用这个方法来控制 创建一个不可见的 固定宽度的
  • 如何清空Keil的历史文件记录

    解决方案 先把Keil 关掉再进行以下操作 1 Win R并输入regedit 2 点开HKEY CURRENT USER 3 点开SOFTWARE 4 点开你使用的KEIL 5 点开Recent File List并删除右侧的文件们 除默
  • 2.4G和蓝牙技术

    转自 http 25078558 blog 163 com blog static 8667921420121012103312364 随着笔记本电脑的普及以及用户的数据传输需求 越来越多的笔记本甚至一些中高端的上网本配备了蓝牙模块 此外

随机推荐

  • Ubuntu16.04安装php5.6

    安装步骤 sudo add apt repository ppa ondrej php sudo apt get install apache2 sudo apt get install mysql server mysql client
  • 【springboot】springboot默认logback日志

    一 问题说明 1 运行时记录日常运行的重要信息 应用报错信息 运维过程数据 2 springboot默认使用日志logback 3 日志级别 1 fatal 灾难信息 合计入error 2 error 记录错误堆栈信息 3 warn 记录运
  • 19年电赛经验总结-应该如何准备电赛

    19年电赛经验总结 应该如何准备电赛 写在前面的话 1 赛前准备 2 比赛经历 3 经验总结 写在最后的话 写在前面的话 最近忙着各大厂的实习面试 趁着准备简历的功夫回顾了一下19年电赛的比赛经历 总体来说还算说得过去 现在把我参加电赛的经
  • 目标检测之GFL

    文章目录 前言 1 GFL的主要创新部分 2 结合代码体现创新部分 1 训练阶段的预测输出 2 将bbox边框的回归值由单一确定值 狄拉克分布 变为一定范围的任意概率分布 3 Distribution Focal Loss 4 Qualit
  • 认识k8s

    本篇文章没有什么强逻辑思维 你看下去就对了 讲下本文背景 博主在面试的过程中 不断被现实捶打 竟然有一次被问及了解k8s吗 我滴妈妈咪呀 我只知道工作中公司用的是k8s 但不知道是啥 被问的一脸懵逼 瞬间给面试官留下的印象从高级降为中级 不
  • 所向披靡的响应式开发——如何一招制胜?前端工程师必会技能

    之前也接触过响应式开发 大概就是下载一个响应式网站模板 然后替换图片 哈哈 确实没有系统的学习和了解过响应式开发 最近工作还蛮清闲 找出压箱底很久的响应式开发教程 大概一月前看过一些 然后也忘得差不多la 从头开始学习呗 这套课程还是很基础
  • Web3的2048,Sui 8192能否打开全链游戏的大门?

    作者 Peng SUN Foresight News Sui 8192 一局游戏就是一个NFT Sui 8192智能合约基于Move语言编写 构成非常简单 包括游戏 Game Board与排行榜 Leaderboard 三部分 覆盖方块移动
  • [keil]L6200E: Symbol XXX multiply defined .O...错误解决方法

    当编译时提示这样的错误时 是因为框出来的变量被重复定义了 我们要从定义的地方解决 1 首先 我们全局搜索这个变量 ctrl f 搜索这个变量在工程中被定义的位置 2 如果发现有多个文件中都有定义过 例如在a c和b c中都有int a 0
  • 电容降压整流电源电路

    https www dianziaihaozhe com dianyuan 924 电容降压整流电源电路通常用于低成本取得非隔离的小电流电源 输出电压一般为几伏到二三十伏 输出电流为几毫安到几十毫安 大多取决于所使用的稳压二极管 所能提供的
  • 机器学习和数据科学从业者必读的10本免费英文书

    本文编译自https www kdnuggets com 2018 05 10 more free must read books for machine learning and data science html 夏天本该是放松的季节
  • android首页底部tab动画,Android实现主页底部菜单中间tab图案凸起

    效果图 因为原来的代码底部导航栏使用的是 LinearLayout ImageView TextView 组合 所以 在这里用到了一个及其重要的属性 android clipChildren false 放在其父布局使其生效 达到想要的效果
  • Python __str__方法

    str 方法 1 不定义 str 方法 使用 print 函数输出对象实例时 默认打印对象实例的内存地址 2 定义 str 方法 使用 print 函数或将对象转换为字符串时 自动调用此方法 3 返回值必须是字符串 str 是Python中
  • centos7上hive3.1.3安装及配置

    1 安装背景 hive是基于hadoop的数据仓库软件 部署运行在linux系统之上 安装之前必须保证hadoop环境运行正常 hive本身不是分布式软件 它的分布式主要是借助hadoop实现 存储是hdfs 计算是mapreduce 需要
  • openGL之API学习(九十三)OpenGL中EXT,ARB扩展

    由于OpenGL的标准更新不是很频繁 因此 当某种技术应用流行起来时 显卡厂商为了支持该技术 会使用自己的扩展来实现该功能 但是不同厂商如果有不同的实现 那么程序编写将会异常繁琐 因此多个厂商共同协商使用一致的扩展 这就是EXT扩展 如果这
  • 时序预测

    时序预测 MATLAB实现CNN BiLSTM卷积双向长短期记忆神经网络时间序列预测 风电功率预测 目录 时序预测 MATLAB实现CNN BiLSTM卷积双向长短期记忆神经网络时间序列预测 风电功率预测 预测效果 基本介绍 程序设计 参考
  • java基础之符号

    hello 大家好 今天 小白将继续分享 如果我说的有什么不妥之处 恳请大佬们指出来 如果大家觉得我写的不错 就给我点个赞鼓励一下 小白在此谢谢各位了 众所周知 程序都是按照着一定的规则进行编写的 这些规则被称为语法 只有语法正确了 程序才
  • C++_STL_常用的拷贝和替换算法

    copy 把A数组拷贝到另一个B数组中 void main copy vector
  • windows控制台命令合集

    转自 微点阅读 https www weidianyuedu com windows控制台命令 大集合 开始 运行 命令 集锦 winver 检查Windows版本 wmimgmt msc 打开windows管理体系结构 WMI wupdm
  • 定位Flutter内存问题很难么?

    简介 flutter内存泄漏定位 作者 闲鱼技术 三莅 内存水位升高导致的稳定性问题严重影响app用户体验 所以开发者们非常关注Flutter的内存表现 随着Flutter业务越来越多 闲鱼也面临着oom导致的crash率提升的问题 下面我
  • 两个经典回文字符串问题中的巧妙算法

    问题一 最长回文子串 给定一个字符串 s 找到 s 中最长的回文子串 第一眼的想法是暴力法 由于其时间复杂度高达O n 3 当s过长时效率会特别低 方法一 中心扩展算法 其思想就是遍历一遍字符串 其中在每一个点都进行以其为中心而均匀展开 分