📜  Manacher算法-线性时间最长回文子串-第2部分

📅  最后修改于: 2021-05-04 16:29:05             🧑  作者: Mango

在Manacher的算法–第1部分中,我们介绍了一些基础知识和LPS长度数组。
在这里,我们将看到如何有效地计算LPS长度数组。

为了有效地计算LPS数组,我们需要了解任何位置的LPS长度如何与任何先前已经计算出的位置的LPS长度值相关。
对于字符串“ abaaba”,我们看到以下内容:

Manacher算法-线性时间最长回文子串

如果我们看看位置3:

  • 位置2和位置4的LPS长度值相同
  • 位置1和位置5的LPS长度值相同

我们从位置0开始从左到右计算LPS长度值,因此我们可以查看是否已经知道位置1、2和3的LPS长度值,那么我们可能不需要计算位置4和5的LPS长度。等于位置3左侧相应位置的LPS长度值。

如果我们环顾位置6:

  • 5位和7位的LPS长度值相同
  • 位置4和位置8的LPS长度值相同

…………。等等。
如果我们已经知道位置1、2、3、4、5和6处的LPS长度值,那么我们可能不需要计算位置7、8、9、10和11处的LPS长度,因为它们等于位置处的LPS长度值位置6左侧的对应位置。
对于字符串“ abababa”,我们看到以下内容:

Manacher算法-线性时间最长回文子串

如果我们已经知道位置1、2、3、4、5、6和7处的LPS长度值,那么我们可能不需要计算位置8、9、10、11、12和13处的LPS长度,因为它们等于LPS长度值位于位置7左侧的相应位置。

您能看到为什么LPS长度值在字符串“ abaaba”中的3、6、9位置对称吗?这是因为这些位置周围有回文子串。位置7周围的字符串“ abababa”也是如此。
在回文中心位置附近的LPS长度值是否始终是对称的(相同的),这是否总是正确的?
答案是否定的。
查看字符串“ abababa”中的位置3和11。两个位置的LPS长度均为3。紧邻的左侧和右侧位置是对称的(值为0),但下一个位置不是对称的。位置1和5(位置3附近)不对称。同样,位置9和13(位置11附近)也不对称。

在这一点上,我们可以看到,如果在某个位置居中的字符串存在回文,那么根据某些情况,围绕中心位置的LPS长度值可能对称也可能不对称。如果我们能够确定左右位置围绕中心位置对称时的情况,则无需计算右位置的LPS长度,因为它与已知的左侧相应位置的LPS值完全相同。我们避免在几个位置进行LPS长度计算的事实使Manacher算法呈线性。

在左右位置在中心位置附近不对称的情况下,我们比较左右两侧的字符以找到回文,但是这里算法也尝试避免某些不进行比较。我们将很快看到所有这些情况。

让我们介绍一些术语以进一步进行:
Manacher算法-线性时间最长回文子串

  • centerPosition –这是要计算LPS长度的位置,比方说centerPosition的LPS长度为d(即L [centerPosition] = d)
  • centerRightPosition –这是与centerPosition相对的位置,而d位置与centerPosition相对(即centerRightPosition = centerPosition + d )
  • centerLeftPosition –这是离开centerPosition的位置和d远离centerPosition的位置(即centerLeftPosition = centerPosition – d )
  • currentRightPosition –这是centerPosition的右侧位置,其LPS长度尚不清楚,必须对其进行计算
  • currentLeftPosition –这是centerPosition左侧的位置,对应于currentRightPosition
    centerPosition – currentLeftPosition = currentRightPosition – centerPosition
    currentLeftPosition = 2 * centerPosition – currentRightPosition
  • 第i个左回文–第i个回文位于centerPosition的左侧,即currentLeftPosition
  • i-right回文集–回文i定位在centerPosition的右边,即currentRightPosition
  • 中心回文– centerPosition的回文

当我们在LPS长度已知的centerPosition时,我们还知道所有小于centerPosition的位置的LPS长度。假设centerPosition的LPS长度为d,即
L [centerPosition] = d

这意味着位置“ centerPosition-d”到“ centerPosition + d”之间的子字符串是回文。
现在,我们进一步计算大于CenterPosition的位置的LPS长度。
假设我们位于currentRightPosition(> centerPosition),需要查找LPS长度。
为此,我们查看已经计算出的currentLeftPosition的LPS长度。

如果currentLeftPosition的LPS长度小于“ centerRightPosition – currentRightPosition”,则currentRightPosition的LPS长度将等于currentLeftPosition的LPS长度。所以
如果L [currentLeftPosition] 案例1 。

让我们考虑以下情况下的字符串“ abababa”:

(单击以清楚地看到它) Manacher算法-线性时间最长回文子串

我们已经计算出LPS长度,直到位置7,其中L [7] = 7,如果我们将位置7视为centerPosition,则centerLeftPosition将为0,centerRightPosition将为14。
现在我们需要计算centerPosition右边其他位置的LPS长度。

对于currentRightPosition = 8,currentLeftPosition为6,L [currentLeftPosition] = 0
同样centerRightPosition – currentRightPosition = 14 – 8 = 6
情况1在这里适用,因此L [currentRightPosition] = L [8] = 0
情况1适用于位置10和12,因此,
L [10] = L [4] = 0
L [12] = L [2] = 0

如果我们查看位置9,则:
currentRightPosition = 9
currentLeftPosition = 2 * centerPosition – currentRightPosition = 2 * 7 – 9 = 5
centerRightPosition – currentRightPosition = 14 – 9 = 5

这里L [currentLeftPosition] = centerRightPosition – currentRightPosition,因此案例1在这里不适用。另请注意,centerRightPosition是字符串的最末端位置。这意味着中心回文是输入字符串的后缀。在这种情况下,L [currentRightPosition] = L [currentLeftPosition]。这是案例2

情况2适用于位置9、11、13和14,因此:
L [9] = L [5] = 5
L [11] = L [3] = 3
L [13] = L [1] = 1
L [14] = L [0] = 0

案例1和案例2到底发生了什么?这只是利用回文对称性而没有任何字符匹配,而是在寻找新位置的LPS长度。

当较大长度的回文包含以其自身中心的左侧为中心的较小长度的回文时,则基于对称性质,将在较大回文中心的右侧以另一个相同的较小回文为中心。如果左侧较小的回文不是较大回文的前缀,则情况1适用;如果它是前缀并且较大的回文是输入字符串本身的后缀,则情况2适用。

如果i左回文完全是i,则当前中心右侧的最长回文(i-右回文)与i到当前中心左侧(i左回文)的最长回文长度一样长。当前中心周围最长的回文中包含的信息(中心回文)和i左回文不是中心回文的前缀(案例1 ),或者(如果i左回文是中心回文的前缀)回文是整个字符串的后缀(案例2 )。

在案例1和案例2中,i右回文的扩展不能超过相应的i左回文的扩展(您能想象为什么它不能进一步扩展吗?),因此i右回文的LPS长度与LPS完全相同左回文的长度。

在这里,第i个左回文和第i个右回文都完全包含在中心回文中(即L [currentLeftPosition] <= centerRightPosition – currentRightPosition)
现在,如果第i个左回文不是中心回文的前缀(L [currentLeftPosition]

如果我们用centerPosition = 11来关注

(单击以清楚地看到它) Manacher算法-线性时间最长回文子串

centerLeftPosition为11 – 9 = 2,centerRightPosition为11 + 9 = 20
如果我们采用currentRightPosition = 15,则currentLeftPosition为7。这里适用情况1,因此L [15] =3。位置7的i-left回文是“ bab”,它完全包含在位置11的中心回文中(即“ dbabcbabd”)。我们可以看到i右回文(位置15)不能比i左回文(位置7)扩展更多。

如果有扩大的可能性,那么i-左回文可能已经扩大了自己。但是没有这样的可能性,因为i-左回文是中心回文的前缀。因此,由于对称性,i-右回文将与i-左回文完全相同,并且无法扩展。在情况1中,这使L [currentRightPosition] = L [currentLeftPosition]。

现在,如果我们考虑centerPosition = 19,则centerLeftPosition = 12和centerRightPosition = 26
如果我们采用currentRightPosition = 23,则currentLeftPosition为15。这里适用情况2,因此L [23] =3。位置15的i-left回文是“ bab”,它完全包含在位置19的中心回文中(即“ babdbab”)。在情况2中,i-左回文是中心回文的前缀,由于中心回文是输入字符串的后缀,所以i-右回文的长度不能超过i-左回文的长度,因此没有更多的字符可以比较和扩展。这使情况2中的L [currentRightPosition] = L [currentLeftPosition]。

情况1:在以下情况下适用L [currentRightPosition] = L [currentLeftPosition]

  • i-左回文完全包含在中心回文中
  • 我左回文不是中心回文的前缀

当满足以上两个条件时
L [currentLeftPosition]

情况2: L [currentRightPosition] = L [currentLeftPosition]在以下情况下适用:

  • i-左回文是中心回文的前缀(也指完全包含在内)
  • 中心回文是输入字符串的后缀

满足以上条件时
L [currentLeftPosition] = centerRightPosition – currentRightPosition(对于第一个条件),并且
centerRightPosition = 2 * N,其中N是输入字符串的长度N(第二条件)。

情况3: L [currentRightPosition]> = L [currentLeftPosition]在以下情况下适用:

  • i-左回文是中心回文的前缀(因此,i-左回文完全包含在中心回文中)
  • 中心回文不是输入字符串的后缀

满足以上条件时
L [currentLeftPosition] = centerRightPosition – currentRightPosition(对于第一个条件),并且
centerRightPosition <2 * N,其中N是输入字符串的长度N(第二条件)。
在这种情况下,有可能发生i右回文扩展,因此i右回文的长度至少与i左回文的长度一样长。

情况4: L [currentRightPosition]> = centerRightPosition –在以下情况下适用currentRightPosition:

  • i-左回文不完全包含在中心回文中

满足以上条件时
L [currentLeftPosition]> centerRightPosition – currentRightPosition
在这种情况下,i右回文的长度至少要等长(centerRightPosition – currentRightPosition),并且i右回文可能会扩展。

在下图中,

(单击以清楚地看到它) Manacher算法-线性时间最长回文子串

如果我们取中心位置7,则案例3在currentRightPosition 11处适用,因为currentLeftPosition 3的i-left回文是中心回文的前缀,而i-right palindrome不是输入字符串的后缀,因此这里L [11] = 9,这是大于I-左回文长度L [3] = 3。在这种情况下,它保证了L [11]将是至少3,因此,在实现中,我们第1集合L [11] = 3,然后我们尝试通过比较从距离4开始的左右字符来扩展它(直到距离3,已经知道字符会匹配)。

如果我们取中心位置11,则案例4适用于currentRightPosition 15,因为L [currentLeftPosition] = L [7] = 7> centerRightPosition – currentRightPosition = 20 – 15 =5。在这种情况下,可以保证L [15]将至少为5,因此,在实现中,我们第1集合L [15] = 5,然后我们试图通过比较在左,右侧字符从远处5开始(如向上至距离5时,已经将其展开知道字符会匹配)。

现在剩下要讨论的一点是,当我们在一个中心位置工作并计算不同rightPositions的LPS长度时,如何知道下一个中心位置将是什么。如果将以currentRightPosition为中心的回文扩展到centerRightPosition之外,则将centerPosition更改为currentRightPosition。

在这里,我们已经看到四种不同的情况,一个头寸的LPS长度将如何取决于前一个头寸的LPS长度。
在第3部分中,我们讨论了它的代码实现,并且以不同的方式研究了这四种情况,并且也实现了这种情况。