OSDN Git Service

style: format markdown files with remark-lint
author24OI-bot <15963390+24OI-bot@users.noreply.github.com>
Sat, 3 Nov 2018 14:50:01 +0000 (22:50 +0800)
committer24OI-bot <15963390+24OI-bot@users.noreply.github.com>
Sat, 3 Nov 2018 14:50:01 +0000 (22:50 +0800)
docs/string/z-function.md

index 6c39cd2..a7f8351 100644 (file)
@@ -22,12 +22,11 @@ Z 函数的形式化定义可被表述为下列基础的 $O(n^2)$ 实现。
 
 ```c++
 vector<int> z_function_trivial(string s) {
-    int n = (int) s.length();
-    vector<int> z(n);
-    for (int i = 1; i < n; ++i)
-        while (i + z[i] < n && s[z[i]] == s[i + z[i]])
-            ++z[i];
-    return z;
+  int n = (int)s.length();
+  vector<int> z(n);
+  for (int i = 1; i < n; ++i)
+    while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
+  return z;
 }
 ```
 
@@ -45,33 +44,33 @@ vector<int> z_function_trivial(string s) {
 
 假设当前下标为 $i$(即我们要计算的下一个 Z 函数值的下标),则有两种情况:
 
-- $i > r$ -- 当前位置在我们已处理位置**之外**。
+-   $i > r$ -- 当前位置在我们已处理位置**之外**。
 
-  我们接下来使用**朴素算法**(即一个一个的比较字符)来计算 $z[i]$。注意如果最后 $z[i] > 0$,我们需要更新最靠右的匹配段的下标,因为新的 $r = i + z[i] - 1$ 一定比之前的 $r$ 优。
+    我们接下来使用**朴素算法**(即一个一个的比较字符)来计算 $z[i]$。注意如果最后 $z[i] > 0$,我们需要更新最靠右的匹配段的下标,因为新的 $r = i + z[i] - 1$ 一定比之前的 $r$ 优。
 
-- $i \le r$ -- 当前位置位于当前匹配段 $[l;r]$ 之内。
+-   $i \le r$ -- 当前位置位于当前匹配段 $[l;r]$ 之内。
 
-  那么我们可以用已计算过的 Z 函数值来“初始化” $z[i]$ 至某值(至少比“从零开始”要好),甚至可能是某些较大的值。
+    那么我们可以用已计算过的 Z 函数值来 “初始化” $z[i]$ 至某值(至少比 “从零开始” 要好),甚至可能是某些较大的值。
 
-  为了做到这一点,我们注意到子串 $s[l\dots r]$ 和 $s[0 \dots r - l]$ 匹配。这意味着作为 $z[i]$ 的一个初始近似,我们可以直接使用对应于段 $s[0 \dots r - l]$ 的已计算过的 Z 函数值,也即 $z[i - l]$。
+    为了做到这一点,我们注意到子串 $s[l\dots r]$ 和 $s[0 \dots r - l]$ 匹配。这意味着作为 $z[i]$ 的一个初始近似,我们可以直接使用对应于段 $s[0 \dots r - l]$ 的已计算过的 Z 函数值,也即 $z[i - l]$。
 
-  然而,$z[i - l]$ 可能太大了:将其应用到位置 $i$ 结果可能超过下标 $r$。这种做法并不合法,原因在于我们对 $r$ 右侧的字符一无所知:他们可能并不满足要求。
+    然而,$z[i - l]$ 可能太大了:将其应用到位置 $i$ 结果可能超过下标 $r$。这种做法并不合法,原因在于我们对 $r$ 右侧的字符一无所知:他们可能并不满足要求。
 
-  此处给出一个相似场景的**例子**:
-  
-  $$
-  s=\mathtt{aaaabaa}
-  $$
-  
-  当我们尝试计算末尾位置($i = 6$)的值时,当前匹配的段为 $[5;6]$。位置 $6$ 会匹配位置 $6 - 5 = 1$,其 Z 函数值为 $z[1] = 3$。显然,我们不能将 $z[6]$ 初始化为 $3$,因为这完全不对。我们可以初始化的最大值为 $1$ -- 因为这是使我们不超过段 $[l;r]$ 的边界 $r$ 的最大可能取值。
+    此处给出一个相似场景的**例子**:
 
-  因此,我们可以放心的将下列值作为 $z[i]$ 的一个初始近似:
-  
-  $$
-  z_0[i] = \min(r - i + 1, z[i - l])
-  $$
-  
-  当将 $z[i]$ 初始化为 $z_0[i]$ 后,我们尝试使用**朴素算法**增加 $z[i]$ 的值 -- 因为宏观来讲,对于边界 $r$ 之后的事情,我们无法得知段是否会继续匹配还是失配。
+    $$
+    s=\mathtt{aaaabaa}
+    $$
+
+    当我们尝试计算末尾位置($i = 6$)的值时,当前匹配的段为 $[5;6]$。位置 $6$ 会匹配位置 $6 - 5 = 1$,其 Z 函数值为 $z[1] = 3$。显然,我们不能将 $z[6]$ 初始化为 $3$,因为这完全不对。我们可以初始化的最大值为 $1$ -- 因为这是使我们不超过段 $[l;r]$ 的边界 $r$ 的最大可能取值。
+
+    因此,我们可以放心的将下列值作为 $z[i]$ 的一个初始近似:
+
+    $$
+    z_0[i] = \min(r - i + 1, z[i - l])
+    $$
+
+    当将 $z[i]$ 初始化为 $z_0[i]$ 后,我们尝试使用**朴素算法**增加 $z[i]$ 的值 -- 因为宏观来讲,对于边界 $r$ 之后的事情,我们无法得知段是否会继续匹配还是失配。
 
 综上所述,整个算法被划分成两种情况,他们只在设置 $z[i]$ 的**初始值**时有所不同:在第一种情况下,其被认为为 $0$,在第二种情况下它由先前已计算过的值确定(使用前述公式)。之后,该算法的两个分支都被规约为实现**朴素算法**。当我们设置完初始值后,该算法即开始执行。
 
@@ -83,17 +82,14 @@ vector<int> z_function_trivial(string s) {
 
 ```c++
 vector<int> z_function(string s) {
-    int n = (int) s.length();
-    vector<int> z(n);
-    for (int i = 1, l = 0, r = 0; i < n; ++i) {
-        if (i <= r)
-            z[i] = min (r - i + 1, z[i - l]);
-        while (i + z[i] < n && s[z[i]] == s[i + z[i]])
-            ++z[i];
-        if (i + z[i] - 1 > r)
-            l = i, r = i + z[i] - 1;
-    }
-    return z;
+  int n = (int)s.length();
+  vector<int> z(n);
+  for (int i = 1, l = 0, r = 0; i < n; ++i) {
+    if (i <= r) z[i] = min(r - i + 1, z[i - l]);
+    while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
+    if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
+  }
+  return z;
 }
 ```
 
@@ -121,31 +117,31 @@ vector<int> z_function(string s) {
 
 为了做到这一点,我们将考虑算法的所有分支:
 
-- $i > r$
+-   $i > r$
 
-  在这种情况下,要么 `while` 循环不进行任何迭代(如果 $s[0] \neq s[i]$),要么其将从位置 $i$ 开始进行若干次迭代,其中每次迭代将向右移动一个字符。每次迭代后,右边界 $r$ 必定被更新。
+    在这种情况下,要么 `while` 循环不进行任何迭代(如果 $s[0] \neq s[i]$),要么其将从位置 $i$ 开始进行若干次迭代,其中每次迭代将向右移动一个字符。每次迭代后,右边界 $r$ 必定被更新。
 
-  因此我们证明了,当 $i > r$ 时,`while` 循环的每轮迭代都会使新的 $r$ 增加 $1$。
+    因此我们证明了,当 $i > r$ 时,`while` 循环的每轮迭代都会使新的 $r$ 增加 $1$。
 
-- $i \le r$
+-   $i \le r$
 
-  在这种情况下,我们将 $z[i]$ 初始化为由前述公式给出的某个具体 $z_0$。将 $z_0$ 和 $r - i + 1$ 比较,可能有三种情况:
+    在这种情况下,我们将 $z[i]$ 初始化为由前述公式给出的某个具体 $z_0$。将 $z_0$ 和 $r - i + 1$ 比较,可能有三种情况:
 
-  - $z_0 < r - i + 1$
+    -   $z_0 < r - i + 1$
 
-    我们证明在这种情况下 `while` 循环不会进行任何迭代。
+        我们证明在这种情况下 `while` 循环不会进行任何迭代。
 
-    这是十分容易证明的,比如通过反证法:如果 `while` 循环进行了至少一次迭代,这意味着初始近似 $z[i] = z_0$ 是不准确的(小于匹配的实际长度)。但是由于 $s[l\dots r]$ 和 $s[0\dots r - l]$ 是一样的,这推出 $z[i - l]$ 的值是错误的(比其该有的值小)。
+        这是十分容易证明的,比如通过反证法:如果 `while` 循环进行了至少一次迭代,这意味着初始近似 $z[i] = z_0$ 是不准确的(小于匹配的实际长度)。但是由于 $s[l\dots r]$ 和 $s[0\dots r - l]$ 是一样的,这推出 $z[i - l]$ 的值是错误的(比其该有的值小)。
 
-    所以,因为 $z[i - l]$ 是正确的且其值小于 $r - i + 1$,故该值同所求的 $z[i]$ 是相同的。
+        所以,因为 $z[i - l]$ 是正确的且其值小于 $r - i + 1$,故该值同所求的 $z[i]$ 是相同的。
 
-  - $z_0 = r - i + 1$
+    -   $z_0 = r - i + 1$
 
-    在这种情况下,`while` 循环可能会进行若干次迭代。因为我们从 $s[r + 1]$ 开始比较,而其位置已经超过了区间 $[l;r]$,故每次迭代都会使 $r$ 增加。
+        在这种情况下,`while` 循环可能会进行若干次迭代。因为我们从 $s[r + 1]$ 开始比较,而其位置已经超过了区间 $[l;r]$,故每次迭代都会使 $r$ 增加。
 
-  - $z_0 > r - i + 1$
+    -   $z_0 > r - i + 1$
 
-    根据 $z_0$ 的定义,这种情况是不可能的。
+        根据 $z_0$ 的定义,这种情况是不可能的。
 
 综上,我们已经证明了内层循环的每次迭代都会使 $r$ 向右移动。由于 $r$ 不可能超过 $n - 1$,这意味着内层循环至多进行 $n - 1$ 轮迭代。
 
@@ -185,7 +181,7 @@ vector<int> z_function(string s) {
 
 ### 字符串压缩
 
-给定一个长度为 $n$ 的字符串 $s$,找到其最短的“压缩”表示,即:寻找一个最短的字符串 $t$,使得 $s$ 可以被 $t$ 的一份或多份拷贝的拼接表示。
+给定一个长度为 $n$ 的字符串 $s$,找到其最短的 “压缩” 表示,即:寻找一个最短的字符串 $t$,使得 $s$ 可以被 $t$ 的一份或多份拷贝的拼接表示。
 
 其中一种解法为:计算 $s$ 的 Z 函数,从小到大循环所有满足 $i$ 整除 $n$ 的 $i$。在找到第一个满足 $i + z[i] = n$ 的 $i$ 时终止。那么该字符串 $s$ 可被压缩为长度 $i​$ 的字符串。
 
@@ -203,4 +199,4 @@ vector<int> z_function(string s) {
 
 * * *
 
-**本页面主要译自博文 [Z-функция строки и её вычисление](http://e-maxx.ru/algo/z_function) 与其英文翻译版 [Z-function and its calculation](https://cp-algorithms.com/string/z-function.html) 。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。**
\ No newline at end of file
+**本页面主要译自博文 [Z-функция строки и её вычисление](http://e-maxx.ru/algo/z_function) 与其英文翻译版 [Z-function and its calculation](https://cp-algorithms.com/string/z-function.html) 。其中俄文版版权协议为 Public Domain + Leave a Link;英文版版权协议为 CC-BY-SA 4.0。**