OSDN Git Service

style: format markdown files with remark-lint
author24OI-bot <15963390+24OI-bot@users.noreply.github.com>
Tue, 6 Nov 2018 14:26:44 +0000 (22:26 +0800)
committer24OI-bot <15963390+24OI-bot@users.noreply.github.com>
Tue, 6 Nov 2018 14:26:44 +0000 (22:26 +0800)
docs/string/manacher.md

index 1dd07ba..dd6944c 100644 (file)
@@ -22,7 +22,7 @@ $$
 
 因此关键思路是,如果以某个位置 $i$ 为中心,我们有一个长度为 $l$ 的回文串,那么我们有以 $i$ 为中心的长度为 $l - 2$,$l - 4$,等等的回文串。所以 $d_1[i]$ 和 $d_2[i]$ 两个数组已经足够表示字符串中所有子回文串的信息。
 
-一个令人惊讶的事实是,存在一个复杂度为线性并且足够简单的算法计算上述两个“回文性质数组” $d_1[]$ 和 $d_2[]$。在这篇文章中我们将详细的描述该算法。
+一个令人惊讶的事实是,存在一个复杂度为线性并且足够简单的算法计算上述两个 “回文性质数组” $d_1[]$ 和 $d_2[]$。在这篇文章中我们将详细的描述该算法。
 
 ## 解法
 
@@ -32,7 +32,7 @@ $$
 
 ## 朴素算法
 
-为了避免在之后的叙述中出现歧义,这里我们指出什么是“朴素算法”。
+为了避免在之后的叙述中出现歧义,这里我们指出什么是 “朴素算法”。
 
 该算法通过下述方式工作:对每个中心位置 $i$,在比较一对对应字符后,只要可能,该算法便尝试将答案加 $1$。
 
@@ -41,7 +41,7 @@ $$
 该朴素算法的实现如下:
 
 ```c++
-vector<int> d1(n),  d2(n);
+vector<int> d1(n), d2(n);
 for (int i = 0; i < n; i++) {
   d1[i] = 1;
   while (0 <= i - d1[i] && i + d1[i] < n && s[i - d1[i]] == s[i + d1[i]]) {
@@ -49,7 +49,8 @@ for (int i = 0; i < n; i++) {
   }
 
   d2[i] = 0;
-  while (0 <= i - d2[i] - 1 && i + d2[i] < n && s[i - d2[i] - 1] == s[i + d2[i]]) {
+  while (0 <= i - d2[i] - 1 && i + d2[i] < n &&
+         s[i - d2[i] - 1] == s[i + d2[i]]) {
     d2[i]++;
   }
 }
@@ -67,7 +68,7 @@ for (int i = 0; i < n; i++) {
 
     因此我们将连续的增加 $d_1[i]$,同时在每一步中检查当前的子串 $[i - d_1[i] \dots i + d_1[i]]$ 是否为一个回文串。如果我们找到了第一处对应字符不同,又或者碰到了 $s$ 的边界,则算法停止。在两种情况下我们均已计算完 $d_1[i]$。此后,仍需记得更新 $(l, r)$。
 
--   现在考虑 $i \le r$ 的情况。我们将尝试从已计算过的 $d_1[]$ 的值中获取一些信息。首先在子回文串 $(l, r)$ 中反转位置 $i$,即我们得到 $j = l + (r - i)$。现在来考察值 $d_1[j]$。因为位置 $j$ 同位置 $i$ 对称,我们**几乎总是**可以置 $d_1[i] = d_1[j]$。该想法的图示如下(可认为以 $j$ 为中心的回文串被“拷贝”至以 $i$ 为中心的位置上):
+-   现在考虑 $i \le r$ 的情况。我们将尝试从已计算过的 $d_1[]$ 的值中获取一些信息。首先在子回文串 $(l, r)$ 中反转位置 $i$,即我们得到 $j = l + (r - i)$。现在来考察值 $d_1[j]$。因为位置 $j$ 同位置 $i$ 对称,我们**几乎总是**可以置 $d_1[i] = d_1[j]$。该想法的图示如下(可认为以 $j$ 为中心的回文串被 “拷贝” 至以 $i$ 为中心的位置上):
 
     $$
     \ldots\ 
@@ -84,12 +85,12 @@ for (int i = 0; i < n; i++) {
     }^\text{palindrome}\ 
     \ldots
     $$
-  
-    然而有一个**棘手的情况**需要被正确处理:当“内部”的回文串到达“外部”回文串的边界时,即 $j - d_1[j] + 1 \le l$(或者等价的说,$i + d_1[j] - 1 \ge r$)。因为在“外部”回文串范围以外的对称性没有保证,因此直接置 $d_1[i] = d_1[j]$ 将是不正确的:我们没有足够的信息来断言在位置 $i$ 的回文串具有同样的长度。
 
-    实际上,为了正确处理这种情况,我们应该“截断”回文串的长度,即置 $d_1[i] = r - i$。之后我们将运行朴素算法以尝试尽可能增加 $d_1[i]$ 的值
+    然而有一个**棘手的情况**需要被正确处理:当 “内部” 的回文串到达 “外部” 回文串的边界时,即 $j - d_1[j] + 1 \le l$(或者等价的说,$i + d_1[j] - 1 \ge r$)。因为在 “外部” 回文串范围以外的对称性没有保证,因此直接置 $d_1[i] = d_1[j]$ 将是不正确的:我们没有足够的信息来断言在位置 $i$ 的回文串具有同样的长度
 
-    该种情况的图示如下(以 $j$ 为中心的回文串已经被截断以落在“外部”回文串内):
+    实际上,为了正确处理这种情况,我们应该 “截断” 回文串的长度,即置 $d_1[i] = r - i$。之后我们将运行朴素算法以尝试尽可能增加 $d_1[i]$ 的值。
+
+    该种情况的图示如下(以 $j$ 为中心的回文串已经被截断以落在 “外部” 回文串内):
 
     $$
     \ldots\ 
@@ -107,7 +108,7 @@ for (int i = 0; i < n; i++) {
     }_\text{try moving here}
     $$
 
-    该图示显示出,尽管以 $j$ 为中心的回文串可能更长,以致于超出“外部”回文串,但在位置 $i$,我们只能利用其完全落在“外部”回文串内的部分。然而位置 $i$ 的答案可能比这个值更大,因此接下来我们将运行朴素算法来尝试将其扩展至“外部”回文串之外,也即标识为 "try moving here" 的区域。
+    该图示显示出,尽管以 $j$ 为中心的回文串可能更长,以致于超出 “外部” 回文串,但在位置 $i$,我们只能利用其完全落在 “外部” 回文串内的部分。然而位置 $i$ 的答案可能比这个值更大,因此接下来我们将运行朴素算法来尝试将其扩展至 “外部” 回文串之外,也即标识为 "try moving here" 的区域。
 
 最后,仍有必要提醒的是,我们应当记得在计算完每个 $d_1[i]$ 后更新值 $(l, r)$。
 
@@ -156,7 +157,7 @@ for (int i = 0, l = 0, r = -1; i < n; i++) {
   d2[i] = k--;
   if (i + k > r) {
     l = i - k - 1;
-    r = i + k ;
+    r = i + k;
   }
 }
 ```
@@ -167,7 +168,7 @@ for (int i = 0, l = 0, r = -1; i < n; i++) {
 
 给定一个长度为 $n$ 的字符串 $s$,我们在其 $n + 1$ 个空中插入分隔符 $\#$,从而构造一个长度为 $2n + 1$ 的字符串 $s'$。举例来说,对于字符串 $s = \mathtt{abababc}$,其对应的 $s' = \mathtt{\#a\#b\#a\#b\#a\#b\#c\#}$。
 
-对于字母间的 $\#$,其实际意义为 $s$ 中对应的“空”。而两端的 $\#$ 则是为了实现的方便。
+对于字母间的 $\#$,其实际意义为 $s$ 中对应的 “空”。而两端的 $\#$ 则是为了实现的方便。
 
 注意到,在对 $s'$ 计算 $d_1[]$ 后,对于一个位置 $i$,$d_1[i]$ 所描述的最长的子回文串必定以 $\#$ 结尾(若以字母结尾,由于字母两侧必定各有一个 $\#$,因此可向外扩展一个得到一个更长的)。因此,对于 $s$ 中一个以字母为中心的极大子回文串,设其长度为 $m + 1$,则其在 $s'$ 中对应一个以相应字母为中心,长度为 $2m + 3$ 的极大子回文串;而对于 $s$ 中一个以空为中心的极大子回文串,设其长度为 $m$,则其在 $s'$ 中对应一个以相应表示空的 $\#$ 为中心,长度为 $2m + 1$ 的极大子回文串(上述两种情况下的 $m$ 均为偶数,但该性质成立与否并不影响结论)。综合以上观察及少许计算后易得,在 $s'$ 中,$d_1[i]$ 表示在 $s​$ 中以对应位置为中心的极大子回文串的**总长度加一**。
 
@@ -180,6 +181,6 @@ for (int i = 0, l = 0, r = -1; i < n; i++) {
 - [UVA #11475 "Extend to Palindrome"](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2470)
 - [P4555 \[国家集训队\] 最长双回文串](https://www.luogu.org/problemnew/show/P4555)
 
-***
+* * *
 
 **本页面主要译自博文 [Нахождение всех подпалиндромов](http://e-maxx.ru/algo/palindromes_count) 与其英文翻译版 [Finding all sub-palindromes in $O(N)$](https://cp-algorithms.com/string/manacher.html) 。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。**