Z algorithmの文章による説明
Z algorithmを図に頼らずに理解したかったので書きました.
長さの文字列
にZ algorithmを適用することを考えます.
以下の説明では文字列中の各文字を0-basedで指定します1.の
文字目を
と表記します.また,
の
文字目から
文字目までを取り出した部分文字列を
と表記します.
のとき
は空文字列とし,空文字列同士は一致するとします.便宜上
を任意の文字に一致しない文字と取り決めます.
に対し,
を
かつ
を満たす
以下の非負整数とします.つまり,
は
と
の最長共通接頭辞の長さを表します.
なので
です.
以下,とし,ある
について
が既知であるとします.また,
とします.
の定義より,
かつ
です.
について,この順に
]を求めることを考えます.
より
を求めるときには
は既知であり,この値を利用します.各
について,
の値により以下のように場合分けして考えます.
のとき
より,
なので以下の2つの関係が成り立ちます.
の
文字目以降の
文字と
の
文字目以降の
文字とが,
の接頭辞と
文字目で不一致となることも含めて一致しているので
であると分かります2.
のとき
及び
より,
が成り立ち,
であると分かります.
を確定するために
と
を比較する必要がありますが,最初の
文字については既に一致していることが判明しているため,比較処理を省略することができます.
Z algorithmではとなる
が見つかった時点で
を
で置き換え, 最初の
文字の比較処理を省略して
を求めることで
が既知である最初の状態に戻します.このようにすることで文字を比較する位置が後退せず,全体の計算量が
になります.
実装例 (Rust)
pub fn z_algorithm<T>(seq: &[T]) -> Vec<usize> where T: Eq, { if seq.is_empty() { return vec![]; } let n = seq.len(); let mut lengths = vec![0; n]; lengths[0] = n; let mut cursor = 1; let mut common_len = 0; while cursor < n { while cursor + common_len < n && seq[cursor + common_len] == seq[common_len] { common_len += 1; } if common_len == 0 { cursor += 1; continue; } lengths[cursor] = common_len; let mut shift = 1; while shift + lengths[shift] < common_len { lengths[cursor + shift] = lengths[shift]; shift += 1; } cursor += shift; common_len -= shift; } lengths }
AtCoderのコンテスト参加環境
AtCoderのコンテストに参加するときの環境 (2025/09/14 現在)
- OS: Ubuntu 24.04 LTS
- エディタ: Visual Studio Code
- 使用言語: Rust
- 自動化ツール: cargo-compete
テンプレート
use proconio::input; fn main() { input! { } }
テンプレートはシンプルにして、必要に応じてUser Snippetsで自作ライブラリなどを貼り付けています。
トリボナッチ数列と行列
トリボナッチ数列の計算*1
フィボナッチ数列に類似した数列にトリボナッチ数列があります.フィボナッチ数列は最初の2項を初期値とし,3項目以降を直前の2項の和として定めていますが,トリボナッチ数列は最初の3項を初期値とし,4項目以降を直前の3項の和として定めています.
トリボナッチ数列は以下のように定義されます.
トリボナッチ数列の具体的な値を以下の表に示します.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | ||
| 0 | 0 | 1 | 1 | 2 | 4 | 7 | 13 | 24 | 44 | 81 | 149 | 274 | 504 | 927 | 1705 | 3136 | 5768 | 10609 | 19513 | 35890 | 66012 | 121415 | 223317 | 410744 | 755476 | 1389537 | 2555757 | 4700770 | 8646064 | 15902591 |
について,
の連続した3つの要素を成分にもつベクトル
を以下のように定義します.
以下の線形変換は
を
に写します.
線形変換は行列により表すことができます.変換を表す行列を
とすると,
の各列には基本ベクトル
をそれぞれ
で変換した
が並びます.これは線形変換において,基本ベクトルの変換先の情報のみを用いて任意のベクトルの変換先を計算できるためです.
より,
となります.
より,は
と同じ変換を表すことが分かります.
上の計算を丁寧に書くと以下のようになります.計算には変換の線形性が利用されています.
による
回の変換は行列の累乗
で表されるので,
は以下の式で計算できます.
ただし,は恒等変換を表す単位行列とします.
より,
は
の第3成分として求めることができます.これは
の
成分に一致します.
例えばは
より,であると分かります.
が大きいとき,
の計算に繰り返し2乗法を用いることで計算の高速化を図ることができます.
一般化
フィボナッチ数列やトリボナッチ数列を一般化した数列として,最初の項を初期値とし,
項目以降を直前の
項の和として定めた数列
を考えます.
は以下の式により求められるベクトルの第
成分として求めることができます.
のとき
はフィボナッチ数列となり,
は以下で計算されるベクトルの第2成分として求めることができます.
のとき
はテトラナッチ数列となり,
は以下で計算されるベクトルの第4成分として求めることができます.