我想写一个python函数来有效地做到这一点:

该函数将接受两个字符串,'a'和'b',并试图找到最长的回文字符串 它可以被形成为使得它是'a'的非空子串和一个非空子串的级联, 子字符串'b'.如果有多个有效答案,它将返回字典中最小的一个. 如果不能形成这样的字符串,它将返回'—1'.

我有一个低效的解决方案,它生成两个字符串的所有子字符串,然后创建所有可能的串联,跟踪最长的有效回文:

def is_palindrome(word):
    """Check if a word is a palindrome."""
    reversed_word = word[::-1]
    return word == reversed_word


def all_substrings_of_word(word):
    """Generate all possible non-empty substrings of a given string."""
    substrings = []
    for sub_string_length in range(1, len(word) + 1):
        for i in range(len(word) - sub_string_length + 1):
            new_word = word[i:i + sub_string_length]
            substrings.append(new_word)
    return substrings


def buildPalindrome(a, b):
    """Attempt to find the longest palindromic string created by concatenating
    a substring of `a` with a substring of `b`."""
    sub_strings_a = all_substrings_of_word(a)
    sub_strings_b = all_substrings_of_word(b)

    # Generate all possible concatenations of substrings from `a` and `b`
    multiplexed_array = [
        word_a + word_b for word_a in sub_strings_a for word_b in sub_strings_b]

    # Find the best palindrome (longest, then lexicographically smallest)
    best_palindrome = ""
    for word in multiplexed_array:
        if is_palindrome(word):
            if len(word) > len(best_palindrome):
                best_palindrome = word
            elif len(word) == len(best_palindrome) and word < best_palindrome:
                best_palindrome = word

    return best_palindrome if best_palindrome else "-1"

print(buildPalindrome("bac", "bac"))   # EXPECTED OUTPUT -- aba
print(buildPalindrome("abc", "def"))   # EXPECTED OUTPUT -- -1
print(buildPalindrome("jdfh", "fds"))   # EXPECTED OUTPUT -- dfhfd

我能请你解释一下如何改进这一点吗?

推荐答案

您可以采用这种方法:

  • b中的所有子字符串构建一个Trie.后缀树会更好,因为它更有效率.

  • 考虑字符串a中潜在回文的所有可能的"中心".因此,这些字符可以是between个两个连续的字符(当回文的大小为偶数时)或on个字符(当回文的大小为奇数时).对于这些中心中的每一个,都要:

    • 仅考虑字符串a,在该中心找到最大的回文p

    • 只要添加的字符(按添加的顺序)是b的Trie中的一个单词,就向左扩展p.这是一个潜在的解决方案.将它与迄今为止最长的回文进行比较,以保持最长的时间.

    • 如果不可能以这种方式扩展p,则shorten p,直到b中存在的字符被删除.在这种情况下,我们有一个潜在的解决方案.

    • 如果在后一种情况下,在p中没有出现在b中的字符,那么我们在所选的中心没有合适的回文.

然后翻转表格并应用上面的程序,其中a变成b的反转,b变成a的反转.这实际上意味着我们在原始b中搜索回文中心.

以下是这个 idea 的一个实现:

# Of the two given strings choose the one that is longest, or if equal, comes first in lexical order
def longest(x, y):
    return min((-len(x), x), (-len(y), y))[1]

def buildTrie(s):
    trie = {}
    for i in range(len(s)):
        node = trie
        for j in range(i, len(s)):
            node = node.setdefault(s[j], {})
    return trie

def buildPalindromeTrincot(s1, s2):
    palin = ""
    # Two passes: one for where the center of a palindrome is in s1, one for the reverse case
    for a, b in ((s1, s2), (s2[::-1], s1[::-1])):
        # Build a trie for B substrings (could be suffixtree for better performance)
        trie = buildTrie(b)
        n = len(a)
        # Visit all possible centers in A for a potential solution of at least 2 characters 
        for center in range(2*n-1, 0, -1):
            # Get the offsets of the innermost characters that must be equal 
            #   for a palindrome of at least two characters
            mid1 = (center - 1)//2
            mid2 = (center + 2)//2
            # Get largest palindrome at this center in A alone: 
            #   `left` will point to the left-neighboring character to that palindrome
            left = next((left for left, right in zip(range(mid1, 0, -1), range(mid2, n)) 
                         if a[left] != a[right]), 
                        max(0, mid1 + mid2 - n))
            # Must extend the palindrome with a substring from B
            node = trie.get(a[left], None)
            if node is not None:  # We can extend the palindrome using B
                for left in range(left-1, -1, -1):
                    if a[left] not in node:
                        left += 1
                        break
                    node = node[a[left]]                    
            else: 
                # See if we can drop characters from the palindrome in A 
                #    until we can replace one with the same character from B
                left = next((left for left in range(left+1, mid1+1) if a[left] in trie), None)
                if left is None:
                    continue  # No solution found here
            palin = longest(a[left:mid2] + a[left:mid1+1][::-1], palin)
                
    return palin or "-1"

对于40个左右的输入字符串,此实现的运行速度比您提供的原始代码快100倍.对于规模为70的投入,这将成为1000倍的倍数.对于长度为500的字符串的输入,此实现在不到一秒的时间内返回答案.

还有改进的空间,比如:

  • 使用后缀树而不是trie
  • 当future 的回文永远不会比已经找到的回文更长时,
  • 让中心从a+b的中心移动并向外"扇形",这样更快地发现一个更大的回文.为此,您需要先构建两个try ,因为您将在一个和另一个之间切换.

但由于上面的代码已经带来了显著的改进,我没有追求这些改进中的任何一项.

Python相关问答推荐

acme错误-Veritas错误:模块收件箱没有属性linear_util'

Pytest两个具有无限循环和await命令的Deliverc函数

运行总计基于多列pandas的分组和总和

如何保持服务器发送的事件连接活动?

什么是合并两个embrame的最佳方法,其中一个有日期范围,另一个有日期没有任何共享列?

无论输入分辨率如何,稳定扩散管道始终输出512 * 512张图像

dask无groupby(ddf. agg([min,max])?''''

处理具有多个独立头的CSV文件

合并与拼接并举

如何在海上配对图中使某些标记周围的黑色边框

Python—为什么我的代码返回一个TypeError

如何在Gekko中使用分层条件约束

Python将一个列值分割成多个列,并保持其余列相同

Python协议不兼容警告

Python OPCUA,modbus通信代码运行3小时后出现RuntimeError

高效生成累积式三角矩阵

Pandas:计数器的滚动和,复位

在使用ROLING()获得最大值时,是否可以排除每个窗口中的前n个值?

try 使用RegEx解析由标识多行文本数据的3行头组成的日志(log)文件

Fake pathlib.使用pyfakefs的类变量中的路径'