Leetcode 3504. Longest Palindrome After Substring Concatenation II

1. 解题思路

这一题是题目3503. Longest Palindrome After Substring Concatenation I的复杂版本,但其实也就是增加了算法复杂度的要求,本质上两题就是同一道题目。

这一题的题意核心其实就是从两个字符串st当中分别取两个可以为空的子串s't'使得两者合并为一个回文数组,然后求其最大长度。

显然,其结果可能包含两种情况:

  1. 最长的回文子串来自于单一字符串s或者t
  2. 最长的回文子串来自于两字符串s或者t的子串的合并结果

其中,对于第一个问题,其本质就是求给定任意一个字符串,求其中的最长回文子串。做法上来说我们就是构造一个反向字符串的字典树,然后依次考察其正向的字符串在其中的最大公共前缀子串是否为一个回文字符串。

然后,对于第二个问题,我们同样就是从s的正向和t找寻一个最大的公共前序子串,那么两者直接拼凑就一定回文字符串,而要令其最长,我们还可以进一步拼凑其后方的最大回文字符串。

其中,对于前者,我们同样可以通过字典树进行处理,对于后者,事实上我们就是要求一个问题:

  • 对于一个给定的字符串s,问其从头部开始能够找到的最长回文子串的长度是多少。

这个我们可以通过使用z算法进行处理。

而对于字典树和z算法,网上已经有很多相关教程了,我自己也写过两篇博客(《经典算法:Trie树结构简介》,《经典算法:Z算法(z algorithm)》)作为备忘了,因此这里就不过多展开了,有兴趣的读者可以自行了解一下。

2. 代码实现

给出python代码实现如下:

class Trie:
    def __init__(self):
        self.trie = {}
    
    def add_word(self, word):
        trie = self.trie
        for c in word:
            trie = trie.setdefault(c, {})
        trie["eos"] = ""

    def find(self, word):
        trie = self.trie
        for c in word:
            if c not in trie:
                return False
            trie = trie[c]
        return "eos" in trie
    
    def find_longest_prefix(self, word):
        prefix = ""
        trie = self.trie
        for c in word:
            if c not in trie:
                break
            prefix += c
            trie = trie[c]
        return prefix

def z_algorithm(s):
    n = len(s)
    z = [0 for _ in range(n)]
    l, r = -1, -1
    for i in range(1, n):
        if i > r:
            l, r = i, i
            while r < n and s[r-l] == s[r]:
                r += 1
            z[i] = r-l
            r -= 1
        else:
            k = i - l
            if z[k] < r - i + 1:
                z[i] = z[k]
            else:
                l = i
                while r < n and s[r-l] == s[r]:
                    r += 1
                z[i] = r-l
                r -= 1
    z[0] = n
    return z

class Solution:
    def longestPalindrome(self, s: str, t: str) -> int:
        n, m = len(s), len(t)
        ans = 0
        
        rs = s[::-1]
        trie_s = Trie()
        trie_rs = Trie()
        for i in range(n):
            trie_s.add_word(s[i:])
            trie_rs.add_word(rs[i:])
        for i in range(n):
            prefix = trie_s.find_longest_prefix(rs[i:])
            if prefix == prefix[::-1]:
                ans = max(ans, len(prefix))
        
        rt = t[::-1]
        trie_t = Trie()
        trie_rt = Trie()
        for i in range(m):
            trie_t.add_word(t[i:])
            trie_rt.add_word(rt[i:])
        for i in range(m):
            prefix = trie_t.find_longest_prefix(t[i:])
            if prefix == prefix[::-1]:
                ans = max(ans, len(prefix))
                
        for i in range(n):
            prefix = trie_rt.find_longest_prefix(s[i:])
            if i+len(prefix) == n:
                ans = max(ans, len(prefix) * 2)
            else:
                remain = s[i+len(prefix):]
                k = len(remain)
                z = z_algorithm(remain + remain[::-1])
                for j in range(k):
                    if z[k+j] == k-j:
                        ans = max(ans, len(prefix) * 2 + k-j)
                        
        for i in range(m):
            prefix = trie_s.find_longest_prefix(rt[i:])
            if i+len(prefix) == m:
                ans = max(ans, len(prefix) * 2)
            else:
                remain = rt[i+len(prefix):]
                k = len(remain)
                z = z_algorithm(remain + remain[::-1])
                for j in range(k):
                    if z[k+j] == k-j:
                        ans = max(ans, len(prefix) * 2 + k-j)
        return ans

提交代码评测得到:耗时6479ms,占用内存331.4MB。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值