题解 | #最长对称子字符串#
最长对称子字符串
https://www.nowcoder.com/practice/93f6c5b032bf473696373ab0d834b0fc
题目链接
题目描述
给定一个字符串(由数字或大小写字母组成),找出其中最长的对称子串。如果存在多个长度相同的最长对称子串,输出任意一个即可。
例如:
- 输入: 
"abbaad" - 输出: 
"abba" - 输入: 
"a1223a" - 输出: 
"22" 
解题思路
寻找最长回文(对称)子串,一个高效且直观的算法是中心扩展法。
该方法的核心思想是,任何一个回文串都有一个中心。这个中心可能是一个字符(对于奇数长度的回文串,如 "aba" 的中心是 "b"),也可能是两个字符之间的空隙(对于偶数长度的回文串,如 "abba" 的中心在两个 "b" 之间)。
算法流程:
- 
遍历所有可能的中心:
对于一个长度为
的字符串,总共有
个潜在的中心(
个字符本身 +
个字符间的空隙)。我们可以通过一次循环遍历完所有这些中心。
 - 
从中心向两边扩展:
- 
在循环中,对于每个索引
i,我们执行两次扩展检查:a. 奇数长度回文:以
s[i]为中心,向两边扩展。初始时,左右指针都指向i。b. 偶数长度回文:以
s[i]和s[i+1]之间的空隙为中心,向两边扩展。初始时,左指针指向i,右指针指向i+1。 - 
扩展的条件是:左右指针没有越界,并且它们指向的字符相同。如果满足条件,就将左指针向左移动一位,右指针向右移动一位,继续比较。
 
 - 
 - 
记录最长回文串:
- 在每次扩展结束后,我们都得到了以当前点为中心的最长回文串的长度。
 - 我们用两个变量,
start和maxLength,来实时记录和更新目前找到的最长回文子串的起始位置和长度。 - 如果在某次扩展后发现了一个更长的回文串,就更新 
start和maxLength。 
 - 
返回结果:
- 遍历完所有可能的中心后,
start和maxLength就对应了最终的最长回文子串。根据这两个值从原字符串中截取子串并返回即可。 
 - 遍历完所有可能的中心后,
 
代码
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 全局变量或通过引用/指针传递来记录最长回文串的起始和长度
int start = 0;
int max_len = 0;
void expand_from_center(const string& s, int left, int right) {
    while (left >= 0 && right < s.length() && s[left] == s[right]) {
        left--;
        right++;
    }
    // 循环结束后, [left+1, right-1] 是回文串
    int current_len = right - left - 1;
    if (current_len > max_len) {
        max_len = current_len;
        start = left + 1;
    }
}
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    string s;
    cin >> s;
    if (s.length() < 2) {
        cout << s << endl;
        return 0;
    }
    
    max_len = 1; // 至少单个字符是回文
    for (int i = 0; i < s.length(); ++i) {
        // 奇数长度
        expand_from_center(s, i, i);
        // 偶数长度
        expand_from_center(s, i, i + 1);
    }
    cout << s.substr(start, max_len) << endl;
    return 0;
}
import java.util.Scanner;
public class Main {
    private int start = 0;
    private int maxLen = 0;
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 2) {
            return s;
        }
        
        maxLen = 1; // 默认单个字符是回文
        for (int i = 0; i < s.length(); i++) {
            // 奇数长度
            expandFromCenter(s, i, i);
            // 偶数长度
            expandFromCenter(s, i, i + 1);
        }
        return s.substring(start, start + maxLen);
    }
    private void expandFromCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        
        int currentLen = right - left - 1;
        if (currentLen > maxLen) {
            maxLen = currentLen;
            start = left + 1;
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        Main solution = new Main();
        System.out.println(solution.longestPalindrome(s));
    }
}
import sys
def solve():
    try:
        s = sys.stdin.readline().strip()
        
        if not s or len(s) < 2:
            print(s)
            return
        start = 0
        max_len = 1
        def expand_from_center(left, right):
            nonlocal start, max_len
            while left >= 0 and right < len(s) and s[left] == s[right]:
                left -= 1
                right += 1
            
            current_len = right - left - 1
            if current_len > max_len:
                max_len = current_len
                start = left + 1
        for i in range(len(s)):
            # 奇数长度
            expand_from_center(i, i)
            # 偶数长度
            expand_from_center(i, i + 1)
        
        print(s[start : start + max_len])
    except (IOError, ValueError):
        return
solve()
算法及复杂度
- 
算法:中心扩展法
 - 
时间复杂度:
,其中
是字符串的长度。我们有
个潜在的中心,从每个中心扩展最多需要
的时间。
 - 
空间复杂度:
。我们只使用了常数个额外的变量来存储状态。
 

