【Swift 算法实战】利用 KMP 算法高效求解最短回文串



网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:极星会首批签约作者

文章目录

摘要

在字符串处理问题中,构造最短回文串是一种常见的面试考察点。本篇文章将探讨如何在字符串前面添加最少的字符,使其成为回文串。我们将使用 KMP(Knuth-Morris-Pratt)部分匹配表 来优化搜索过程,使其时间复杂度达到 O(n)。最后,我们通过示例代码进行测试,并分析其时间和空间复杂度。

描述

给定一个字符串 s,可以在字符串前面添加字符,使其转换为 最短回文串,并返回最终结果。例如:

  • 示例 1:

    复制代码
    输入: "aacecaaa"
    输出: "aaacecaaa"
  • 示例 2:

    复制代码
    输入: "abcd"
    输出: "dcbabcd"

约束条件:

  • 0 <= s.length <= 5 * 10^4
  • s 仅包含小写英文字母。

题解答案

KMP 最长前缀匹配

思路:

  1. 构造辅助字符串 :我们将 s 反转,并构造 newStr = s + "#" + reversed(s)。
  2. 构造 LPS(最长前缀后缀匹配)数组 :
    • LPS 数组的最后一个元素代表 s 本身已经是回文的最大部分,其余部分需要添加到 s 前面。
  3. 生成结果 :
    • 计算 prefixToAdd = rev.prefix(s.count - LPS.last!)
    • 返回 prefixToAdd + s

题解代码分析

swift 复制代码
import Foundation

class Solution {
    func shortestPalindrome(_ s: String) -> String {
        if s.isEmpty { return s }

        let rev = String(s.reversed())
        let newStr = s + "#" + rev
        let lps = computeLPS(newStr)
        let prefixToAdd = rev.prefix(s.count - lps.last!)

        return prefixToAdd + s
    }

    private func computeLPS(_ s: String) -> [Int] {
        let chars = Array(s)
        let n = chars.count
        var lps = [Int](repeating: 0, count: n)
        var length = 0
        var i = 1

        while i < n {
            if chars[i] == chars[length] {
                length += 1
                lps[i] = length
                i += 1
            } else {
                if length > 0 {
                    length = lps[length - 1]
                } else {
                    lps[i] = 0
                    i += 1
                }
            }
        }

        return lps
    }
}

代码解读:

  • computeLPS() :
    • 构建 KMP 的 最长前缀后缀匹配表 (LPS)。
    • lps[i] 表示 s[0...i] 子串的最长前缀后缀匹配长度。
    • 通过 while 遍历整个字符串,维护 前缀长度 变量 length,高效计算 LPS 数组。
  • shortestPalindrome() :
    • 计算 s + "#" + reversed(s) 的 LPS。
    • 计算需要添加的前缀 prefixToAdd,拼接后返回最终结果。

示例测试及结果

swift 复制代码
let solution = Solution()
print(solution.shortestPalindrome("aacecaaa")) // 输出: "aaacecaaa"
print(solution.shortestPalindrome("abcd"))    // 输出: "dcbabcd"
print(solution.shortestPalindrome("race"))    // 输出: "ecarace"
print(solution.shortestPalindrome("a"))       // 输出: "a"
print(solution.shortestPalindrome(""))        // 输出: ""

测试结果:

txt 复制代码
aaacecaaa
dcbabcd
ecarace
a
""

时间复杂度

  1. 计算 reversed(s):O(n)
  2. 构造 newStr:O(n)
  3. 计算 LPS 数组(KMP 前缀匹配): O(n)
  4. 拼接结果:O(n)

总时间复杂度:O(n)

空间复杂度

  • 额外的 LPS 数组:O(n)
  • 反转字符串 rev 和 newStr 需要额外存储:O(n)

总空间复杂度:O(n)

总结

本篇文章详细介绍了 最短回文串 的求解方案,并使用 KMP 前缀匹配表 (LPS) 优化计算。相较于暴力解法,KMP 方案可以在 O(n) 时间内 找到最优解,是较为高效的方式。

未来展望

  • 优化 LPS 计算:使用更精简的构造方法来降低空间开销。
  • 扩展到回文分割问题 :考虑如何最小化字符插入次数,使 s 变为回文。
  • 适用于多种字符集:扩展算法,使其支持 Unicode 字符串。

参考资料

相关推荐
林恒smileZAZ32 分钟前
浏览器为什么加载不了超大图片
前端·vue.js·html5
java_nnnn1 小时前
LeetCode刷题滑动窗口(水果成篮+找到字符串中所有字母的异位词)Java
java·开发语言·leetcode
小羊没烦恼!1 小时前
异步编程:线程概述及使用
java·开发语言·mysql·算法·c#
wanderist.1 小时前
202609CSP认证P3
算法
云淡风轻~窗明几净2 小时前
角谷猜想最接近严格证明的测试 Version1.1.1
人工智能·算法·机器学习·图论
机器学习之心2 小时前
PINN × SVM × NSGA-II × 熵权TOPSIS:一套跑通「建模—优化—决策」的多目标优化框架
算法·机器学习·支持向量机
别动我齐刘海3 小时前
机器人 C++ / 系统联调补强2——异常处理、Protobuf
linux·c++·人工智能·学习·算法·机器学习·机器人
文丑颜不良啊3 小时前
LeetCode276~300题目解析
java·leetcode
荆棘鸟智能3 小时前
短历时洪水预报算法实践:水文模型与 LightGBM 残差订正融合
人工智能·算法·机器人
葫三生4 小时前
关于三生原理系列被技术开发社区批量收录或引发策展边界讨论的一种思考
大数据·人工智能·深度学习·算法·机器学习·数据挖掘·开源