📌  相关文章
📜  最长子字符串,其字符可以重新排列以形成回文(1)

📅  最后修改于: 2023-12-03 15:40:17.134000             🧑  作者: Mango

最长可重排回文子字符串

给定一个字符串,找到其中最长的子字符串,使其字符可以被重新排列成一个回文。如果有多个这样的子字符串,则返回任何一个即可。

思路

一个字符串可以被重新排列成一个回文当且仅当该字符串中最多只有一个字符的出现次数是奇数。因此,我们可以通过统计每个字符出现的次数来判断一个字符串是否能被重排成一个回文。具体实现时,我们可以使用一个哈希表来统计每个字符出现的次数。然后,我们遍历哈希表中的每个计数器,并计算它们中的偶数部分。由于这些偶数部分可以被任意地排列,所以我们可以将它们全部加起来以得到重新排列后可以构成回文的字符数量。

当然,题目要求最长的子字符串,我们可以通过双指针法来解决。具体地,我们在字符串中使用双指针来维护一个窗口。窗口的右边界不断右移,直到窗口中的字符不再能够构成一个回文子串。此时,我们记录下当前窗口的长度并左移窗口的左边界,直到窗口中的字符能够构成一个回文子串。这样,我们就找到了以当前右端点为结尾的最长的可重排回文子字符串。

代码
def longest_palindrome(s: str) -> str:
    # 计数每个字符出现的次数
    counter = [0] * 128
    for c in s:
        counter[ord(c)] += 1
        
    # 计算可重排成回文的字符数量
    even_cnt = 0
    for cnt in counter:
        even_cnt += cnt // 2 * 2
        
    # 构造回文字符串
    res = [''] * len(s)
    mid = len(s) // 2
    i, j = mid - 1 if len(s) % 2 == 0 else mid, mid
    for c, cnt in enumerate(counter):
        while cnt > 1:
            res[i], res[j] = chr(c), chr(c)
            i, j = i - 1, j + 1
            cnt -= 2
        if cnt == 1:
            res[mid] = chr(c)
    
    return ''.join(res)

def longest_palindrome_substring(s: str) -> str:
    if not s:
        return ''
    
    # 找到最长可重排回文子串
    left, right = 0, 0
    counter = [0] * 128
    while right < len(s):
        # 扩大窗口
        counter[ord(s[right])] += 1
        while not all(cnt % 2 == 0 for cnt in counter):
            # 缩小窗口
            counter[ord(s[left])] -= 1
            left += 1
        if right - left + 1 > len(longest):
            longest = s[left:right+1]
        right += 1
        
    return longest
复杂度分析
  • 时间复杂度:$O(n)$,其中 $n$ 是字符串的长度。我们需要遍历字符串一次来统计字符出现次数,再遍历一次字符串来找到最长可重排回文子串。遍历字符串的过程为 $O(n)$,因此总时间复杂度为 $O(n)$。
  • 空间复杂度:$O(1)$,因为我们只需要常数个额外变量来记录统计字符出现次数的哈希表和构造最长可重排回文子串的数组。