336. Palindrome Pairs 回文对

给定一组唯一的单词, 找出所有不同 的索引对(i, j),使得列表中的两个单词, words[i] + words[j] ,可拼接成回文串。

示例 1:

输入: [“abcd”,“dcba”,“lls”,“s”,“sssll”]
输出: [[0,1],[1,0],[3,2],[2,4]]
解释: 可拼接成的回文串为 [“dcbaabcd”,“abcddcba”,“slls”,“llssssll”]

示例 2:

输入: [“bat”,“tab”,“cat”]
输出: [[0,1],[1,0]]
解释: 可拼接成的回文串为 [“battab”,“tabbat”]

暴力枚举

本题可以想到暴力做法,我们枚举每一对字符串的组合,暴力判断它们是否能够构成回文串即可。时间复杂度 O(n2×m),其中 n 是字符串的数量,m 是字符串的平均长度。时间复杂度并不理想,考虑进行优化。

Code

    def palindromePairs(self, words: List[str]) -> List[List[int]]:
        ans = []
        for i in range(len(words)):
            for j in range(len(words)):
                if i != j:
                    temp = words[i] + words[j]
                    if temp == temp[::-1]:
                        ans.append([i, j])
        return ans

字典树/哈希表+前后缀

假设存在两个字符串 s1 和 s2,s1+s2 是一个回文串,记这两个字符串的长度分别为 len1 和 len2,我们分三种情况进行讨论:

  1. len1=len2,这种情况下 s1 是 s2 的翻转。
  2. len1>len2,这种情况下我们可以将 s1 拆成左右两部分:t1 和 t2,其中 t1 是 s2 的翻转,t2是一个回文串。
  3. len1<len2,这种情况下我们可以将 s2 拆成左右两部分:t1 和 t2,其中 t2 是 s1 的翻转,t1是一个回文串。

这样,对于每一个字符串,我们令其为 s1 和 s2 中较长的那一个,然后找到可能和它构成回文串的字符串即可。

具体地说,我们枚举每一个字符串 kkk,令其为 s1s_1s1s2s_2s2 中较长的那一个,那么 kkk 可以被拆分为两部分,t1t_1t1t2t_2t2

  1. t1t_1t1 是回文串时,符合情况 333,我们只需要查询给定的字符串序列中是否包含 t2t_2t2 的翻转。
  2. t2t_2t2 是回文串时,符合情况 222,我们只需要查询给定的字符串序列中是否包含 t1t_1t1 的翻转。

也就是说,我们要枚举字符串 kkk 的每一个前缀和后缀,判断其是否为回文串。如果是回文串,我们就查询其剩余部分的翻转是否在给定的字符串序列中出现即可。

注意到空串也是回文串,所以我们可以将 kkk 拆解为 k+∅k+\varnothingk+∅+k\varnothing+k+k,这样我们就能将情况 111 也解释为特殊的情况 222 或情况 333

而要实现这些操作,我们只需要设计一个能够在一系列字符串中查询「某个字符串的子串的翻转」是否存在的数据结构,有两种实现方法:

  • 我们可以使用字典树存储所有的字符串。在进行查询时,我们将待查询串的子串逆序地在字典树上进行遍历,即可判断其是否存在。

  • 我们可以使用哈希表存储所有字符串的翻转串。在进行查询时,我们判断带查询串的子串是否在哈希表中出现,就等价于判断了其翻转是否存在。

字典树 Code

class Node:
    def __init__(self):
        self.ch = [0] * 26
        self.flag = -1

class Solution:
    def palindromePairs(self, words: List[str]) -> List[List[int]]:
        tree = [Node()]

        def insert(s: str, index: int):
            length = len(s)
            add = 0
            for i in range(length):
                x = ord(s[i]) - ord("a")
                if tree[add].ch[x] == 0:
                    tree.append(Node())
                    tree[add].ch[x] = len(tree) - 1
                add = tree[add].ch[x]
            tree[add].flag = index
        
        def findWord(s: str, left: int, right: int) -> int:
            add = 0
            for i in range(right, left - 1, -1):
                x = ord(s[i]) - ord("a")
                if tree[add].ch[x] == 0:
                    return -1
                add = tree[add].ch[x]
            return tree[add].flag
        
        def isPalindrome(s: str, left: int, right: int) -> bool:
            length = right - left + 1
            return length < 0 or all(s[left + i] == s[right - i] for i in range(length // 2))
        
        n = len(words)
        for i, word in enumerate(words):
            insert(word, i)
        
        ret = list()
        for i, word in enumerate(words):
            m = len(word)
            for j in range(m + 1):
                if isPalindrome(word, j, m - 1):
                    leftId = findWord(word, 0, j - 1)
                    if leftId != -1 and leftId != i:
                        ret.append([i, leftId])
                if j and isPalindrome(word, 0, j - 1):
                    rightId = findWord(word, j, m - 1)
                    if rightId != -1 and rightId != i:
                        ret.append([rightId, i])

        return ret

哈希表 Code

    def palindromePairs(self, words: List[str]) -> List[List[int]]:
        def findWord(s: str, left: int, right: int) -> int:
            return indices.get(s[left: right + 1], -1)

        def isPalindrome(s: str, left: int, right: int) -> bool:
            sub = s[left: right + 1]
            return sub == sub[::-1]

        n, ret = len(words), list()
        indices = {word[::-1]: i for i, word in enumerate(words)}

        for i, word in enumerate(words):
            m = len(word)
            for j in range(m + 1):
                if isPalindrome(word, j, m - 1):
                    leftId = findWord(word, 0, j - 1)
                    if leftId != -1 and leftId != i:
                        ret.append([i, leftId])
                if j and isPalindrome(word, 0, j - 1):
                    rightId = findWord(word, j, m - 1)
                    if rightId != -1 and rightId != i:
                        ret.append([rightId, i])
        return ret

猜你喜欢

转载自blog.csdn.net/weixin_43336281/article/details/107830513