当前位置: 首页
AI教程
字符串变为回文最少添加字符进阶解法

字符串变为回文最少添加字符进阶解法

热心网友 时间:2026-06-26
转载

在字符串处理算法中,有一道经典高频面试题:给定一个字符串,允许在任意位置添加字符,要求计算最少添加多少个字符能使整个字符串成为回文串,并输出其中一种合法结果。这道题本身就具备一定挑战性,而其进阶版本额外引入了最长回文子序列字符串(strlps),目标是在已知该子序列的前提下进一步降低时间复杂度。本文将详细拆解这道进阶问题的解法,帮助读者深入理解“剥洋葱”式的高效思路。

题目与进阶要求

原问题:给定一个字符串str,返回在添加字符最少的情况下,使str整体成为回文字符串的任意一种结果。
进阶问题:额外提供str的最长回文子序列字符串strlps,要求设计一种比原问题时间复杂度更低的算法。

该题难度标记为“困难”,但一旦掌握核心思路,就会发现它其实是一个层层剥离的“剥洋葱”过程,直观且高效,适合在面试或竞赛中快速实现。

解题思路详解

如果已拥有最长回文子序列字符串strlps,算法时间复杂度可优化至O(N)。设str长度为N,strlps长度为M,最终回文串的总长度为2×N − M。解法类似于逐层剥洋葱:每次处理当前最外层的一对字符,并填充到结果数组的两端。

以下通过具体例子演示完整步骤:
str = "A1BC22DE1F",strlps = "1221"。首先创建结果数组res,长度为2×N − M。

洋葱的第0层由strlps[0]和strlps[M-1]组成,即"1...1"。从str最左侧开始查找字符'1',发现字符'A'位于str第0位,'1'位于第1位,因此左侧第0层洋葱圈外的部分为"A",记作leftPart。从str最右侧开始查找字符'1',右侧第0层洋葱圈外的部分为"F",记作rightPart。将(leftPart + rightPart的逆序)复制到res左侧未赋值的位置,将(rightPart + leftPart逆序)复制到res右侧未赋值的位置,此时res变为"AF..FA"。然后将洋葱的第0层"1"复制到res左右两侧未赋值的位置,res变成"AF1..1FA"。至此,第0层被剥离。

洋葱的第1层由strlps[1]和strlps[M-2]组成,即"2...2"。从str左侧第0层洋葱向右查找'2',发现左侧第1层洋葱圈外的部分为"BC",记作leftPart。从str右侧第0层洋葱向左查找'2',右侧第1层洋葱圈外的部分为"DE",记作rightPart。同样操作:将(leftPart + rightPart逆序)复制到res左侧,将(rightPart + leftPart逆序)复制到res右侧,res变为"AF1BCED..DECB1FA"。然后复制第1层"2",res变成"AF1BCED2..2DECB1FA"。第1层剥离完毕,洋葱被完全剥开,最终结果"AF1BCED22DECB1FA"。

整个流程的核心是不断定位当前洋葱层的左、右外部分,将(leftPart + rightPart逆序)填入res左侧,将(rightPart + leftPart逆序)填入res右侧,直至所有洋葱层处理完毕。理解这一逻辑后,代码实现便水到渠成。

完整代码实现(Java)

package live.every.day.ProgrammingDesign.CodingInterviewGuide.String;

/**
 * 添加最少字符使字符串整体变成回文串
 *
 * 【原题】
 * 给定字符串str,可在任意位置添加字符,返回添加字符最少的情况下使str整体为回文串的一种结果。
 *
 * 【进阶题】
 * 给定str及其最长回文子序列字符串strlps,返回添加最少字符使str整体为回文串的一种结果。
 * 进阶问题多了一个参数,要求时间复杂度低于原问题的实现。
 *
 * 【难度】
 * 困难
 *
 * 【解答】
 * 进阶问题解法。
 *
 * 已知strlps后,时间复杂度可降至O(N)。设str长度为N,strlps长度为M,最终回文串长度为2×N - M。
 * 本解法采用“剥洋葱”思想,下面通过示例说明:
 *
 * str="A1BC22DE1F",strlps="1221"。res长度为2×N - M。
 * 洋葱第0层由strlps[0]和strlps[M-1]组成,即"1...1"。从str最左侧找'1',发现'A'是str[0],'1'是str[1],
 * 因此左侧第0层外部分为"A",记为leftPart。从str最右侧找'1',右侧第0层外部分为"F",记为rightPart。
 * 将(leftPart+rightPart逆序)复制到res左侧,将(rightPart+leftPart逆序)复制到res右侧,res变为"AF..FA"。
 * 再将洋葱第0层复制到res左右两侧,res变为"AF1..1FA"。第0层剥离完毕。
 * 洋葱第1层由strlps[1]和strlps[M-2]组成,即"2...2"。从str左侧第0层向右找'2',左侧第1层外部分为"BC",记为leftPart。
 * 从str右侧第0层向左找'2',右侧第1层外部分为"DE",记为rightPart。
 * 将(leftPart+rightPart逆序)复制到res左侧,将(rightPart+leftPart逆序)复制到res右侧,res变为"AF1BCED..DECB1FA"。
 * 再将洋葱第1层复制到res左右两侧,res变为"AF1BCED2..2DECB1FA"。第1层剥离完毕,洋葱剥完,返回"AF1BCED22DECB1FA"。
 *
 * 整个过程反复寻找洋葱圈的左、右外部分,将(leftPart+rightPart逆序)填入res左侧,将(rightPart+leftPart逆序)填入res右侧,
 * 直至所有层处理完成。具体实现参见下方getPalindrome2方法。
 *
 * @author Created by LiveEveryDay
 */
public class AddMinCharsMakePalindrome2 {

    public static String getPalindrome2(String str, String strlps) {
        if (str == null || str.equals("")) {
            return "";
        }
        char[] chas = str.toCharArray();
        char[] lps = strlps.toCharArray();
        char[] res = new char[2 * chas.length - lps.length];
        int chasLeft = 0;
        int chasRight = chas.length - 1;
        int lpsLeft = 0;
        int lpsRight = lps.length - 1;
        int resLeft = 0;
        int resRight = res.length - 1;
        int tmpLeft = 0;
        int tmpRight = 0;
        while (lpsLeft <= lpsRight) {
            tmpLeft = chasLeft;
            tmpRight = chasRight;
            while (chas[chasLeft] != lps[lpsLeft]) {
                chasLeft++;
            }
            while (chas[chasRight] != lps[lpsRight]) {
                chasRight--;
            }
            set(res, resLeft, resRight, chas, tmpLeft, chasLeft, chasRight, tmpRight);
            resLeft += chasLeft - tmpLeft + tmpRight - chasRight;
            resRight -= chasLeft - tmpLeft + tmpRight - chasRight;
            res[resLeft++] = chas[chasLeft++];
            res[resRight--] = chas[chasRight--];
            lpsLeft++;
            lpsRight--;
        }
        return String.valueOf(res);
    }

    private static void set(char[] res, int resl, int resr, char[] chas, int ls, int le, int rs, int re) {
        for (int i = ls; i < le; i++) {
            res[resl++] = chas[i];
            res[resr--] = chas[i];
        }
        for (int i = re; i > rs; i--) {
            res[resl++] = chas[i];
            res[resr--] = chas[i];
        }
    }

    public static void main(String[] args) {
        String str = "A1B21C";
        String strlps = "121";
        System.out.printf("The palindrome is: %s", getPalindrome2(str, strlps));
    }
}

// ------ 输出结果 ------
/*
The palindrome is: AC1B2B1CA
*/
来源:https://blog.csdn.net/chimomo/article/details/129758569

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
科研人员必读:多肽、蛋白质、重组蛋白区别及定制指南

科研人员必读:多肽、蛋白质、重组蛋白区别及定制指南

Section 01 多肽 VS 蛋白质 VS 重组蛋白 多肽、蛋白质和重组蛋白,本质上是同宗同源的东西——都是氨基酸串起来的生物大分子。三者的核心区别,说到底无非是三个维度:分子大小、折叠形态,以及生产方式。 接下来是一张清晰的对比图,帮你快速建立直觉: ![对比图1](https:

时间:2026-07-07 17:55
知识图谱与本体语义建模的核心区别解析

知识图谱与本体语义建模的核心区别解析

谈到人工智能如何“理解”知识,有两个概念常被放在一起讨论:知识图谱与本体语义建模。不少人以为它们是同一事物,或者认为后者是前者的进化版。实际上,两者的分工完全不同——打个比方,一个是“记事的本子”,另一个是“写本子之前先定好的规矩”。 1 本体语义建模:先绘制一张“通用分类蓝图” 设想一下,你要整

时间:2026-07-07 17:55
强烈推荐工作搭子WorkBuddy

强烈推荐工作搭子WorkBuddy

一次偶然的机会,从朋友那里了解到WorkBuddy这个工具。说实话,在AI产品扎堆的今天,能遇到一个下载即用的助手,确实值得推荐给每一个被日常琐事缠身的人。 安装过程没什么难度,双击安装包默认安装即可。需要留意的是,如果在Windows7上折腾了半天没反应,别慌——这工具在高版本Windows下运行

时间:2026-07-07 17:55
跨境电商系统自动化测试与CI/CD流水线构建指南

跨境电商系统自动化测试与CI/CD流水线构建指南

技术方向:自动化测试与DevOps实践关键词:日本代购、一站式日淘、雅虎代拍系统、煤炉自动代拍 一、测试分层策略详解 不少人刚开始就想直接搞E2E测试,觉得跑通完整流程才够“真实”。然而,测试金字塔这么多年仍不过时,原因很简单——不同层级的测试各有分工,缺少任何一层都会不稳。来看看这张金字塔图: ┌

时间:2026-07-07 17:54
中小企业AI营销矩阵工具推荐:赛诺贝斯智域蒲公英

中小企业AI营销矩阵工具推荐:赛诺贝斯智域蒲公英

天天刷着别人的爆款内容,自己却“有心无力”——这才是2026年绝大多数中小企业运营社交媒体的真实写照。说白了,社交媒体如今早已不是“要不要做”的选择题,而是“怎么做才能真正见效”的生存考验。现实情况是,团队人力就那么几个,预算也紧巴巴,却要同时运营抖音、小红书、知乎、头条、百家号等多个阵地……文案、

时间:2026-07-07 17:54
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全