【问题标题】:Efficient way to verify if a string is a rotated palindrome?验证字符串是否为旋转回文的有效方法?
【发布时间】:2012-09-05 16:43:24
【问题描述】:

旋转回文类似于“1234321”、“3432112”。 天真的方法会将字符串切割成不同的片段并将它们连接回来,看看字符串是否是回文。 这将需要 O(n^2),因为有 n 个切割,并且对于每个切割,我们需要 O(n) 来检查字符串是否是回文。 我想知道是否有比这更好的解决方案。 我想是的,请指教。

谢谢!

【问题讨论】:

  • 问题:单个数字是回文吗?这是123217898 旋转回文吗?(换句话说,它可以切出两个以上的子串)
  • 123217898 不是旋转回文。

标签: algorithm palindrome


【解决方案1】:

根据这篇维基百科文章,可以在 O(n) 时间内为每个长度为 n 的字符串 S 计算相同大小的数组 A,这样:

如果 S 的长度为 i 的前缀是回文,则 A[i]==1。

http://en.wikipedia.org/wiki/Longest_palindromic_substring

算法应该可以在以下位置找到:

Manacher, Glenn (1975),“一种新的线性时间“在线”算法 找到一个字符串的最小初始回文数"

换句话说,我们可以在线性时间内检查字符串的哪些前缀是回文。我们将使用这个结果来解决提出的问题。

每个(非旋转)回文 S 具有以下形式 S = psxs^Rp^R。

其中“x”是回文的中心(空字符串或一个字母字符串), “p”和“s”是(可能是空的)字符串,“s^R”表示“s”字符串颠倒。

从这个字符串创建的每个旋转回文都具有以下两种形式之一(对于某些 p):

  1. sxs^Rp^Rp
  2. p^Rpsxs^R

确实如此,因为您可以选择是在回文中间之前还是之后剪切一些子字符串,然后将其粘贴到另一端。

正如我们所看到的,子串“p^Rp”和“sxs^R”都是回文,其中一个是偶数长度,另一个是奇数长度,如果 S 是奇数长度。

我们可以使用维基百科链接中提到的算法来创建两个数组 A 和 B。数组 A 是通过检查哪些前缀是回文而 B 是后缀来创建的。然后我们搜索一个值 i 使得 A[i]==B[i]==1 使得前缀或后缀具有偶数长度。如果提议的字符串是旋转回文并且偶数部分是“p^Rp”子字符串,我们将找到这样的索引,因此我们可以通过将该字符串的一半移动到字符串的另一端轻松恢复原始回文。


rks 对解决方案的一个评论,这个解决方案不起作用,对于字符串 S = 1121,它将创建长度大于或等于 S 长度的回文字符串 11211121,但它不是旋转的回文。如果我们更改解决方案以检查是否存在长度等于 S 长度的回文,它会起作用,但我没有看到任何直接的解决方案如何更改搜索最长子字符串的算法,使其将搜索固定长度的子字符串 (len(S))。 (我没有在解决方案下写这个作为评论,因为我是 Stackoverflow 的新手并且没有足够的声誉这样做)


第二句话——很抱歉没有包含 Manacher 的算法,如果有人链接到算法的想法或某些实现,请将其包含在 cmets 中。

【讨论】:

  • 哦,是的,谢谢你的反例!我自己没有找到一个,所以我认为我的算法是正确的。至于“我看不到如何更改搜索最长子字符串的算法”,您始终可以获取所有回文,然后只取大小合适的回文。但是,如果使用相等而不是 >=,算法是否正确?我不确定了。
  • 是的,相等就足够了,假设您从旋转的回文 X 创建一个字符串 XX 并且找到的长度为 n 的回文字符串 S 从连接词中的索引 i 开始,它在 n 结束+i 如果你把从 n 开始到 n+i 结束的子字符串放在字符串前面,你会得到字符串 X。
  • 到另一句话。我不确定“你总是可以得到所有回文”,因为在最坏的情况下有 O(n^2) 可能的回文,所以这可能会打破时间限制。但据我阅读和理解的算法,它使用了一些聪明的内部回文可能是什么样子,所以可能有一些方法可以改变它,但除非有人会告诉我如何改变它,否则我不会自动期望它是微不足道的。
  • 因为我仍然无法在您的解决方案下编写,您可能应该以这样的方式编辑它,很明显我们仍然需要找到一种算法来解决“长度正好为 n 的回文子串”的问题”,因为不清楚这种算法是否只是“最长回文子串问题”的一个微不足道的变体,如果是的话,可能会很有趣。
【解决方案2】:

将字符串连接到自身,然后在新字符串中进行经典回文研究。如果你找到一个长度大于或等于原始字符串长度的回文,你就知道你的字符串是一个旋转的回文。

对于您的示例,您将在 34321123432112 中进行研究,发现 21123432112,它比您的初始字符串长,因此它是一个旋转回文。

编辑:正如 Richard Stefanec 所指出的,我的算法在 1121 上失败,他建议我们将 >= 的长度测试更改为 =

EDIT2:应该注意的是,找到给定大小的回文显然并不容易。阅读 Richard Stefanec 帖子下的讨论以了解更多信息。

【讨论】:

【解决方案3】:
#Given a string, check if it is a rotation of a palindrome. 
#For example your function should return true for “aab” as it is a rotation of “aba”.
string1 = input("Enter the first string")
def check_palindrome(string1):
    #string1_list = [word1 for word1 in string1]
    #print(string1_list)
    string1_rotated = string1[1::1] + string1[0]
    print(string1_rotated)
    string1_rotated_palindrome = string1_rotated[-1::-1]
    print(string1_rotated_palindrome)
    if string1_rotated == string1_rotated_palindrome:
        return True
    else:
        return False
isPalindrome = check_palindrome(string1)
if(isPalindrome):
    print("Rotated string is palindrome as well")
else:
    print("Rotated string is not palindrome")

【讨论】:

    【解决方案4】:

    我想提出一种简单的解决方案,只使用传统算法。它不会解决任何更难的问题,但它应该足以完成您的任务。它与其他两个提议的解决方案有些相似,但似乎都不够简洁,我无法仔细阅读。

    第一步:将字符串连接到自身(abvc - > abvcabvc),就像在所有其他建议的解决方案中一样。

    第二步:对新获得的字符串及其反转做Rabin-Karp precalculation(使用滚动哈希)。

    第三步:让字符串的长度为n。对于每个索引iin0...n-1,使用 Rabin-Karp 预计算,检查加倍字符串[i, i + n - 1] 的子字符串是否在恒定时间内为回文(基本上正向和反向子字符串的获得值应该平等)。

    结论:如果第三步发现任何回文 - 则字符串是旋转回文。如果不是 - 那么它不是。

    PS:Rabin Karp 使用哈希,即使是不重合的字符串也可能发生冲突。因此,如果哈希检查引起了相等性的验证暴力检查,这是一个好主意。尽管如此,如果 Rabin Karp 中使用的散列函数是好的,那么解决方案的摊销速度应该保持O(n)

    【讨论】:

      【解决方案5】:

      您可以将相同的模式添加到原始模式的末尾。比如pattern是1234321,那么你可以在12343211234321的末尾添加相同的pattern。完成后,你可以使用KMP或其他子串匹配算法来找到你想要的字符串。如果匹配,则返回 ture。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-07-11
        • 1970-01-01
        • 2023-01-18
        • 2019-11-15
        • 1970-01-01
        • 2019-05-08
        • 1970-01-01
        相关资源
        最近更新 更多