游乐游手机版
首页/AI教程/文章详情

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

时间:2026-06-26 16:55
给定字符串及其最长回文子序列,采用剥洋葱策略:先找到最长回文子序列,然后从外向内逐层处理,将每层左右两侧的字符及其逆序依次填入结果数组的对应位置,最终生成一个包含原字符串且添加字符最少的回文串,算法时间复杂度为O(N)。

在字符串处理算法中,有一道经典高频面试题:给定一个字符串,允许在任意位置添加字符,要求计算最少添加多少个字符能使整个字符串成为回文串,并输出其中一种合法结果。这道题本身就具备一定挑战性,而其进阶版本额外引入了最长回文子序列字符串(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
上一篇五秒AI出图全球最快Stable Diffusion教程 下一篇阿里云ECS环境Hermes Agent搭建与百炼Token Plan接入及故障修复
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Windows Docker Desktop RabbitMQ生产级部署完整指南
AI教程 · 2026-06-29

Windows Docker Desktop RabbitMQ生产级部署完整指南

前言 在 Windows 本地开发环境中,直接安装 RabbitMQ 确实颇为周折:需要单独配置 Erlang 运行环境、手动管理环境变量、服务启停全凭手工操作。更令人困扰的是,版本兼容冲突、端口占用、环境不一致等问题层出不穷。笔者见过不少开发者为搭建环境就得耗费整整半天时间。 相比之下,借助 Do

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践
AI教程 · 2026-06-29

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践

先分享一个切实感受。过去两年,我们与福建制造企业合作较为频繁,发现一个非常突出的现象:超过80%的企业官网,产品参数仍然存放在PDF或图片中。AI爬虫?根本无法抓取。这些企业技术实力不弱、资质证照齐全、应用案例也丰富,但在AI搜索这一全新战场上,它们几乎处于隐身状态。 一、一个正在发生的行业变化 A

阿里云Token Plan团队版功能价格与省钱购买指南
AI教程 · 2026-06-29

阿里云Token Plan团队版功能价格与省钱购买指南

阿里云百炼近期推出了名为“Token Plan 团队版”的全新服务,这一服务专为企业与开发者量身打造,定位为AI大模型订阅平台。通过引入Credits作为统一计量单位,将文本生成、图像生成等多模态AI能力纳入单一计费体系,同时无缝兼容主流AI编程工具及智能体(Agent)生态系统。其核心亮点包括:全

阿里云物联网.NET Core客户端位置信息上报
AI教程 · 2026-06-29

阿里云物联网.NET Core客户端位置信息上报

阿里云物联网平台的位置服务并非一个完全独立的功能模块。位置信息可包含二维坐标与三维坐标,而位置数据的来源本质上是借助设备属性进行上传。换言之,若要让设备上报位置,您需先将其视为一个普通属性进行处理。 1)添加二维位置数据 操作过程十分简洁。进入数据分析 → 空间数据可视化 → 二维数据,点击添加,将

年阿里云服务器选型配置与网站部署全攻略
AI教程 · 2026-06-29

年阿里云服务器选型配置与网站部署全攻略

2026年,阿里云服务器生态已高度成熟,形成了清晰的轻量应用服务器与ECS云服务器两大产品阵营。无论你是计划搭建个人博客、企业官网,还是运营电商平台、进行应用开发,基本都能找到理想的解决方案。本指南将从服务器选型、配置选择、部署流程到安全运维,系统梳理2026年最实用的操作要点,帮助你少走弯路,让网