Leetcode 3504. Longest Palindrome After Substring Concatenation II
- Leetcode 3504. Longest Palindrome After Substring Concatenation II
- 1. 解题思路
- 2. 代码实现
- 题目链接:3504. Longest Palindrome After Substring Concatenation II
1. 解题思路
这一题是题目3503. Longest Palindrome After Substring Concatenation I的复杂版本,但其实也就是增加了算法复杂度的要求,本质上两题就是同一道题目。
这一题的题意核心其实就是从两个字符串s
和t
当中分别取两个可以为空的子串s'
和t'
使得两者合并为一个回文数组,然后求其最大长度。
显然,其结果可能包含两种情况:
- 最长的回文子串来自于单一字符串
s
或者t
- 最长的回文子串来自于两字符串
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。