动态规划求解最短回文字符串构造成本

云强小哥_5131

云强小哥_5131

2026-07-01

474人浏览

原创

动态规划求解最短回文字符串构造成本

本文讲解如何使用动态规划解决“最小成本构造指定长度回文字符串”问题:给定合法相邻字符对(rune pairs)及其代价,求构造长度恰好为 k 的回文字符串的最小总代价;若不可行则返回 -1。

本文讲解如何使用动态规划解决“最小成本构造指定长度回文字符串”问题:给定合法相邻字符对(rune pairs)及其代价,求构造长度恰好为 k 的回文字符串的最小总代价;若不可行则返回 -1。

该问题本质是带约束的最优化回文构造问题,核心在于:

  • 字符串必须是回文(正读反读一致);
  • 所有相邻字符对(即长度为 2 的子串)必须在输入中显式给出,且代价已知;
  • 总代价 = 字符串中所有连续二元组(s[0:2], s[1:2], ..., s[k−2:k])的代价之和;
  • 目标:在满足长度 k 和回文约束下,最小化总代价。

⚠️ 关键理解澄清(针对原文困惑):

  • 字符串长度 k 指字符数(如 "abba" 长度为 4),而非 rune pair 数量;
  • 一个长度为 k 的字符串包含 k−1 个重叠的相邻字符对(即 s[i:i+2],i 从 0 到 k−2);
  • 例如 "abbaacaabba"(长度 11)含 10 个 rune pairs:ab, bb, ba, aa, ac, ca, aa, ab, bb, ba —— 但题干样例误写为 abbaacaabba. 并只列出 6 个,实为笔误;正确解析应为全部 10 个重叠对。
  • 回文约束意味着:若字符串为 s[0..k−1],则对所有 i ∈ [0, k−1],有 s[i] == s[k−1−i]。

因此,构造过程不能暴力枚举所有回文(26ᵏ 量级),而应利用回文对称性 + 动态规划

✅ 正确解法思路(DP 状态设计)

由于回文由中心向两侧扩展,且代价仅依赖相邻字符,我们按「回文半径」或「区间长度」设计状态更自然:

  • 定义 dp[l][r][a][b] 表示:构造回文子串 s[l..r](闭区间),其中 s[l] = a、s[r] = b 时的最小代价。
    但 k ≤ 100,四维状态空间过大(100×100×26×26 ≈ 6.7M),可优化。

更高效方式:按回文长度从小到大 DP,只记录两端字符

设 dp[len][a][b] = 构造长度为 len 的回文字符串,且首尾字符分别为 a 和 b 的最小代价(注意:因回文,必有 a == b 当 len 为奇数且 a,b 是最外层;但更通用做法是固定 s[0]=a, s[len−1]=b,由回文约束得 a==b)。

绘蛙AI视频
绘蛙AI视频

绘蛙AI视频是一款面向电商营销的图片转视频和模特动态视频生成工具。

下载

✅ 实际推荐状态:
dp[i][j] = 构造长度为 i 的回文,且最外层字符对为 j(即 s[0]s[i−1])的最小代价。
但需支持内部递归填充。

更标准且简洁的做法是:
令 dp[l][r] 表示回文区间 [l, r] 的最小构造代价,其中 s[l] 和 s[r] 已确定(由转移决定)
但输入未给出单字符代价,只给二元组代价 → 所有代价均来自相邻对,因此:

  • 长度为 1 的字符串:无相邻对 → 代价为 0(但题目要求 k ≥ 2,故无需考虑);
  • 长度为 2 的回文:形如 "aa", "bb"…,代价 = cost["aa"](若存在);
  • 长度为 3 的回文:形如 "aba",含对 "ab" 和 "ba" → 代价 = cost["ab"] + cost["ba"];
  • 长度为 4 的回文:"abba" → 对 "ab", "bb", "ba" → 代价 = cost["ab"] + cost["bb"] + cost["ba"]。

观察发现:任意回文 s 的代价 = 所有 s[i:i+2](i=0..k−2)代价之和。
而回文结构意味着:
s[0:k] 是回文 ⇔ s[i] = s[k−1−i],因此 s[i:i+2] 与 s[k−2−i:k−i] 存在镜像关系,但代价仍需独立累加(因每对位置不同)。

✅ 最优子结构:
要构造长度为 k 的回文 s,可考虑其最外层两个字符 x 和 y(必有 x == y),则:

  • 若 k == 2:s = "xx",代价 = cost["xx"](若存在);
  • 若 k == 3:s = "xyx",代价 = cost["xy"] + cost["yx"];
  • 若 k ≥ 4:s = "x" + t + "x",其中 t 是长度为 k−2 的回文,且 t 的首字符必须与 "x" 拼接成合法对 "xt[0]",末字符同理(因 s[1] = t[0], s[k−2] = t[k−3],且 s[0:2]="xt[0]", s[k−2:k]="t[k−3]x")。

因此,定义:
dp[length][first][last] = 构造长度为 length 的回文,首字符为 first、尾字符为 last 的最小代价(由回文性质,first 必须等于 last,故可简化为 dp[len][c] 表示首尾均为字符 c 的最小代价)。

但中间部分 t 的首尾也受约束:t 本身是回文,且 s[0:2] = "c" + t[0] 必须存在代价,s[len−2:len] = t[-1] + "c" 也必须存在代价。

故状态应为:
dp[l][a][b] = 构造长度为 l 的回文,且 s[0] = a, s[l−1] = b 的最小代价。
由回文 ⇒ a == b,所以实际只需 dp[l][a],但转移时需知道 s[1] 和 s[l−2](即 t 的首尾)以查 cost["a"+s[1]] 和 cost[s[l−2]+"a"]。

最终推荐实现(Python 伪代码):

from collections import defaultdict
import sys

# 输入解析
n, k = map(int, input().split())
cost = {}
for _ in range(n):
    line = input().split()
    pair, c = line[0], int(line[1])
    cost[pair] = c

# dp[l][a][b] = min cost to build palindrome of length l with s[0]==a, s[l-1]==b
# Since palindrome => a must equal b, we use dp[l][a] but store transitions via inner chars
# Instead, use dp[l][i][j]: i,j are 0..25 (a->0, z->25), meaning s[0]=chr(i+'a'), s[l-1]=chr(j+'a')
# Initialize with inf
INF = float('inf')
dp = [[[INF] * 26 for _ in range(26)] for _ in range(k + 1)]

# Base case: length 2
for a in range(26):
    for b in range(26):
        pair = chr(a + ord('a')) + chr(b + ord('a'))
        if pair in cost:
            if a == b:  # "aa" is palindrome
                dp[2][a][b] = cost[pair]

# Base case: length 3 -> "aba": need "ab" and "ba"
for a in range(26):
    for b in range(26):
        ab = chr(a + ord('a')) + chr(b + ord('a'))
        ba = chr(b + ord('a')) + chr(a + ord('a'))
        if ab in cost and ba in cost:
            dp[3][a][a] = cost[ab] + cost[ba]  # s[0]=a, s[2]=a, s[1]=b

# Fill for length l from 4 to k
for l in range(4, k + 1):
    for a in range(26):  # s[0] and s[l-1] must both be a
        for mid_first in range(26):   # s[1]
            for mid_last in range(26): # s[l-2], must equal mid_first for palindrome? No: s[l-2] must equal s[1] only if l=4; generally s[1] == s[l-2] by palindrome
            # Actually: for palindrome s[0..l-1], s[1] == s[l-2], s[2] == s[l-3], etc.
            # So inner substring s[1:l-1] is itself a palindrome of length l-2, with s[1] == s[l-2]
            # Thus: dp[l][a][a] = min over b of { cost["ab"] + dp[l-2][b][b] + cost["ba"] }
            # where "ab" = s[0:2], "ba" = s[l-2:l]
            b = mid_first
            ab = chr(a + ord('a')) + chr(b + ord('a'))
            ba = chr(b + ord('a')) + chr(a + ord('a'))
            if ab in cost and ba in cost and dp[l-2][b][b] != INF:
                new_cost = cost[ab] + dp[l-2][b][b] + cost[ba]
                if new_cost <p>? 注意事项:  </p>
  • 字符映射:小写字母 'a'..'z' → 0..25,便于数组索引;
  • 初始化 dp 为无穷大,仅对合法 rune pair 更新 base cases;
  • 转移时严格检查 ab 和 ba 是否存在于 cost 字典中;
  • 时间复杂度:O(k × 26³),k ≤ 100 → 最多约 100 × 17,576 ≈ 1.76M,完全可行;
  • 若 k 为奇数,中间字符不影响相邻对数量,但被自然包含在 dp[l-2] 的构造中(如 "aba" 中 "ab" 和 "ba" 已覆盖全部两对)。

总结:本题不是字符串模拟或回溯生成,而是利用回文对称性将问题分解为重叠子问题的动态规划。关键突破点在于——认识到长度为 k 的回文的代价 = 外层一对代价 + 内层回文代价 + 另一外层一对代价,从而建立 O(k·26²) 或 O(k·26³) 的高效解法。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

2023.07.20

1551

4

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

2023.07.25

3624

7

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.31

1549

3

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

2023.08.03

20717

23

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

2567

5

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

2627

5

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

1063

5

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.10

576

4

python是前端还是后端
python是前端还是后端

Python属于前端也属于后端,其灵活性和丰富的生态系统使得开发人员能够在不同的领域中灵活运用。本专题为大家提供python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2023

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.1万人学习