中心扩展法

本文介绍了如何利用中心拓展法解决LeetCode上的《最长回文子串》问题。代码实现中,首先初始化结果数组,然后通过遍历字符串并尝试以每个字符为中心进行奇数长度和偶数长度的回文串拓展,比较并更新最长回文子串的起始位置、结束位置和长度。最后返回最长回文子串。这种方法考虑了回文串长度的奇偶性,确保了所有可能的中心点都被检查到。

题目:

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

目的

求回文子串

由于我们事先无法确定最长回文子串的长度奇偶性,所以每次扩展都要取两次中心点,再取两者的扩展结果中长度较大的那个。代码如下,比较容易理解。

中心拓展法就是从中间向外拓展,用两个指针l和r,分别向前和向后遍历,如果两个指针的值不同,则停止遍历,如果相同,则代表一个回文字符串的诞生,继续遍历,直到边界。

注意:这里有一个需要注意的地方,就是字符串长度可以是奇数也可以是偶数
当回文串长度为奇数时,对称中心是具体的字符,如 “abababa”

但是当回文串长度为偶数时,对称中心会位于两个字符的间隙,如 abaaba -> aba|aba

以下标3来看,第一个字符串abababa的值为b
第一个字符串第一个应比较的应该为[a,a],跳过其本身,因为字符串长度为奇数,其本身无需比较。

以下标2来看,第二个字符串abaaba的值为a。
第二个字符串第一个应比较的应该为[a,a],其本身并不能跳过,因为字符串长度为偶数,其本身需要比较。若字符串为ababba,则不是回文串了。

代码

class Solution {
    public String longestPalindrome(String s){
    	if(s == null || s.length() == 0){
            return "";
        }
        
 	    // 数组第一位记录起始位置,第二位记录结束位置,第三位记录长度(单侧)
        int[] res = new int[3];
        
        for(int i = 0;i < s.length();i++){
            int[] odd = expand(s,i,i);
            int[] even = expand(s,i,i+1);    
            int[] max = odd[2] > even[2] ? odd : even;
        	if(max[2]>res[2]){
                res = max;
            }
        }
        return s.substring(res[0],res[1]+1);
    }

    
    private int[] expand(String s,int l,int r){
    	while( l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)){
            l--;
            r++;
        }
        l++;
        r--;
    	return  new int[]{l,r,r-l};
    }
}
### **中心扩展法分步引导** --- #### **第一步:理解回文串的对称性** 回文串的核心特点是**对称**: - **奇数长度**:如 `"bab"`,对称中心是单个字符 `'a'`。 - **偶数长度**:如 `"baab"`,对称中心是两个相同的字符 `'aa'`。 **关键思考**: 1. 如果从中心向两边扩展,只要左右字符相同,就能逐步得到更长的回文串。 2. 需要分别处理奇数和偶数长度的中心。 --- #### **第二步:设计扩展函数** 定义一个辅助函数,从中心向左右扩展,直到不匹配为止: ```python def expand(s, left, right): # 向左右扩展,直到越界或字符不匹配 while left >= 0 and right < len(s) and s[left] == s[right]: left -= 1 right += 1 # 返回当前最长回文子串 return s[left + 1 : right] ``` **解释**: - 参数 `left` 和 `right` 是中心点的左右起始位置。 - 循环终止时,`left` 和 `right` 指向不匹配的位置,因此实际回文串是 `[left+1 : right]`。 --- #### **第三步:遍历所有可能的中心** 对每个字符,尝试以它为中心(奇数)和以它及下一个字符为中心(偶数): ```python def longestPalindrome(s): res = "" for i in range(len(s)): # 奇数长度中心(单字符) odd = expand(s, i, i) # 偶数长度中心(双字符) even = expand(s, i, i + 1) # 更新最长结果 res = max(res, odd, even, key=len) return res ``` **关键点**: - `expand(s, i, i)`:以 `s[i]` 为中心的奇数长度扩展。 - `expand(s, i, i + 1)`:以 `s[i]` 和 `s[i+1]` 为中心的偶数长度扩展。 - `max(key=len)`:始终保留最长的子串。 --- #### **第四步:验证示例** 以 `s = "babad"` 为例: 1. `i=0`(字符 `'b'`): - 奇数扩展:`"b"` → `res = "b"` - 偶数扩展:`"ba"`(不匹配) → 跳过 2. `i=1`(字符 `'a'`): - 奇数扩展:`"a"` → `"bab"`(最长) → `res = "bab"` - 偶数扩展:`"aa"`(不匹配) → 跳过 3. `i=2`(字符 `'b'`): - 奇数扩展:`"b"` → `"aba"` → `res = "bab"`(长度相同) - 偶数扩展:`"ba"`(不匹配) → 跳过 4. 最终结果:`"bab"`。 --- #### **第五步:复杂度分析** - **时间复杂度**:\( O(n^2) \) - 遍历每个字符(`n` 次),每次扩展最多 `n/2` 次。 - **空间复杂度**:\( O(1) \) - 只用了常数额外空间。 --- ### **完整代码实现** ```python def longestPalindrome(s): def expand(left, right): while left >= 0 and right < len(s) and s[left] == s[right]: left -= 1 right += 1 return s[left + 1 : right] res = "" for i in range(len(s)): odd = expand(i, i) # 奇数长度 even = expand(i, i + 1) # 偶数长度 res = max(res, odd, even, key=len) return res ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值