lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
  1846 随笔 :: 0 文章 :: 109 评论 :: 288万 阅读

1. 题目

 

 

 

https://leetcode.cn/problems/longest-palindromic-substring/description/

 

2. 解法

总有三种解法

 

动态规划、中心扩展和马拉车算法这三种解法。根据网上的资料,我总结了以下几点:

  • 动态规划的思路是用一个二维数组 dp[i][j] 表示字符串从 i 到 j 的子串是否是回文的,然后根据状态转移方程 dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1] 来更新 dp 数组,并记录最长的回文子串。这种方法的时间复杂度和空间复杂度都是 O(n^2) ,其中 n 是字符串的长度。
  • 中心扩展的思路是遍历字符串的每个位置,以该位置为中心,向两边扩展,判断是否是回文,并记录最长的回文子串。需要注意的是,中心位置可能在一个字符上,也可能在两个字符之间,所以需要分别处理奇数长度和偶数长度的回文。这种方法的时间复杂度是 O(n^2) ,空间复杂度是 O(1) 。
  • 马拉车算法的思路是先对字符串进行预处理,用特殊字符(如 # )隔开每个字符,使得原来的奇数长度和偶数长度的回文都变成奇数长度。然后用一个数组 P[i] 表示以 i 为中心的最长回文半径,利用回文的对称性和已经计算过的 P 值,避免了一些不必要的扩展。同时维护一个当前已知的最右回文边界 R 和对应的中心 C ,如果 i 在 R 内,那么可以利用 i 关于 C 的对称点 i’ 的 P 值来初始化 P[i] ,否则令 P[i] = 0 。然后再以 i 为中心扩展,并更新 R 和 C 。最后遍历 P 数组,找出最大的 P 值,就是最长回文子串的长度。这种方法的时间复杂度是 O(n) ,空间复杂度是 O(n) 。

 

中心扩展

主要思路是:

  • 首先判断字符串是否为空或长度为1,如果是,直接返回原字符串,因为它本身就是回文。
  • 然后初始化两个变量 start 和 end ,用来记录最长回文子串的起始和终止位置。
  • 接着遍历字符串的每个位置 i ,以该位置为中心,向两边扩展,寻找最长的回文子串。
  • 对于每个位置 i ,有两种情况需要考虑:一种是以 i 为中心的奇数长度的回文,另一种是以 i 和 i+1 为中心的偶数长度的回文。因此,需要调用两次辅助函数 expandAroundCenter ,分别传入不同的参数,得到两种情况下的回文长度 len1 和 len2 ,然后取两者中较大的值作为当前位置的回文长度 len 。
  • 如果当前位置的回文长度 len 大于之前记录的最大长度 end - start ,说明找到了一个更长的回文子串,那么就更新 start 和 end 的值,使得它们能够包含当前的回文子串。具体地, start = i - (len - 1) / 2 , end = i + len / 2 。这里需要注意一下奇偶数的处理,可以自己画图验证一下。
  • 最后,当遍历完所有位置后,返回最长回文子串,即 s.substring(start, end + 1) 。这里要注意 substring 的用法,它是左闭右开区间,所以要加上 1 。

 

具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Solution {
    public String longestPalindrome(String s) {
        // 如果字符串为空或长度为1,直接返回原字符串
        if (s == null || s.length() < 2) {
            return s;
        }
        // 初始化最长回文子串的起始和终止位置
        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);
            // 如果当前回文长度大于之前记录的最大长度,更新最长回文子串的起始和终止位置
            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) {
        // 当左右指针在字符串范围内,并且左右字符相等时,继续扩展
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        // 返回回文的长度
        return right - left - 1;
    }
}

  

3. 总结

posted on   白露~  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
历史上的今天:
2021-04-28 深入JVM关闭与关闭钩子
2021-04-28 ShutdownHook - Java 优雅停机解决方案
2021-04-28 RocketMQ性能优化
2021-04-28 RocketMQ调优心得总结
2021-04-28 RocketMQ性能优化【实战笔记】
2021-04-28 RocketMQ在面试中那些常见问题及答案+汇总
2019-04-28 24种设计模式的通俗理解
点击右上角即可分享
微信分享提示