1. 概述

在本篇文章中,我们将介绍几种在给定字符串中找出所有回文子串的方法,并分析每种方法的时间复杂度。

2. 暴力遍历法

暴力法是最直观的解法:我们遍历字符串的所有子串,并逐一判断每个子串是否是回文。

public Set<String> findAllPalindromesUsingBruteForceApproach(String input) {
    Set<String> palindromes = new HashSet<>();
    for (int i = 0; i < input.length(); i++) {
        for (int j = i + 1; j <= input.length(); j++) {
            if (isPalindrome(input.substring(i, j))) {
                palindromes.add(input.substring(i, j));
            }
        }
    }
    return palindromes;
}

其中,判断是否为回文的方法如下:

private boolean isPalindrome(String input) {
    StringBuilder plain = new StringBuilder(input);
    StringBuilder reverse = plain.reverse();
    return (reverse.toString()).equals(input);
}

当然,判断回文还有其他更高效的方法可以选用。

时间复杂度:O(n³)
❌ 对于大文本来说性能堪忧,不推荐用于高频或大数据量场景。

3. 中心扩展法

中心扩展法的核心思想是:将每个字符(或两个字符之间)作为回文的中心,向两边扩展,从而找出所有回文子串。

public Set<String> findAllPalindromesUsingCenter(String input) {
    Set<String> palindromes = new HashSet<>();
    for (int i = 0; i < input.length(); i++) {
        palindromes.addAll(findPalindromes(input, i, i + 1)); // 偶数长度
        palindromes.addAll(findPalindromes(input, i, i));     // 奇数长度
    }
    return palindromes;
}

具体扩展逻辑如下:

private Set<String> findPalindromes(String input, int low, int high) {
    Set<String> result = new HashSet<>();
    while (low >= 0 && high < input.length() && input.charAt(low) == input.charAt(high)) {
        result.add(input.substring(low, high + 1));
        low--;
        high++;
    }
    return result;
}

时间复杂度:O(n²)
⚠️ 相比暴力法已经优化不少,适合中等规模数据处理。

4. Manacher 算法

Manacher 算法可以在线性时间复杂度内找出最长回文子串。我们也可以用它来找出所有的回文子串。

4.1 初始化处理

首先,我们在字符串首尾加上特殊字符(如 @#),便于统一处理奇偶长度回文:

String formattedInput = "@" + input + "#";
char inputCharArr[] = formattedInput.toCharArray();

接着,定义一个二维数组 radius,分别记录奇数和偶数长度的回文半径:

int radius[][] = new int[2][input.length() + 1];

4.2 核心逻辑

遍历字符串,填充 radius 数组:

Set<String> palindromes = new HashSet<>();
int max;
for (int j = 0; j <= 1; j++) {
    radius[j][0] = max = 0;
    int i = 1;
    while (i <= input.length()) {
        palindromes.add(Character.toString(inputCharArr[i]));
        while (inputCharArr[i - max - 1] == inputCharArr[i + j + max])
            max++;
        radius[j][i] = max;
        int k = 1;
        while ((radius[j][i - k] != max - k) && (k < max)) {
            radius[j][i + k] = Math.min(radius[j][i - k], max - k);
            k++;
        }
        max = Math.max(max - k, 0);
        i += k;
    }
}

4.3 提取所有回文子串

最后,通过 radius 数组提取所有回文子串:

for (int i = 1; i <= input.length(); i++) {
    for (int j = 0; j <= 1; j++) {
        for (max = radius[j][i]; max > 0; max--) {
            palindromes.add(input.substring(i - max - 1, max + j + i - 1));
        }
    }
}

✅ **时间复杂度:O(n)**(理论上)
⚠️ 但实际提取所有回文子串时,由于 substring() 每次都创建新字符串,整体复杂度会退化为 **O(n²)**。这一点与低效的字符串拼接类似。

5. 总结

本文介绍了三种查找回文子串的方法:

方法 时间复杂度 是否推荐
暴力法 O(n³)
中心扩展法 O(n²)
Manacher 算法 O(n) 理论上 ⚠️ 实际应用需注意性能陷阱

所有示例代码均可在 GitHub 获取。


原始标题:Find Substrings That Are Palindromes in Java