公式
B - 本棚の並び順 / Order of Books on the Bookshelf 解説 by admin
gpt-5.3-codex概要
長さ \(N\) の列 \(A\) の中に、長さ \(M\) の列 \(B\) と完全一致する連続部分列があるかを探す問題です。
制約が大きいため、文字列検索で有名な KMP 法 を使って \(O(N+M)\) で判定します。
考察
この問題は「配列版の部分文字列検索」です。
素朴に全開始位置 \(i\)(最大 \(N-M+1\) 個)について、毎回 \(M\) 個比較すると、計算量は
[ O((N-M+1)\times M) \approx O(NM) ]
になります。
最大で \(N,M \le 5\times 10^5\) なので、最悪 \(2.5\times 10^{11}\) 比較となり、到底間に合いません(TLE)。
そこで、比較に失敗したときの情報を再利用できる KMP 法 を使います。
KMP のポイントは次の 2 段階です。
- パターン \(B\) について、prefix function(失敗関数)\(\pi\) を作る
- \(\pi[i]\) は「\(B[0..i]\) の接頭辞と接尾辞が一致する最大長」
- \(A\) を左から 1 回走査し、失敗時に \(\pi\) を使って比較位置をジャンプする
- これにより同じ要素を何度も無駄に比較しない
これで全体を線形時間にできます。
アルゴリズム
- 入力を受け取る。
- パターン配列 \(B\) から \(\pi\) 配列を構築する。
jを現在一致している長さとして持ち、- 不一致なら
j = pi[j-1]で戻る処理を繰り返す。
- 配列 \(A\) を左から走査して KMP 検索を行う。
- 同様に不一致時は
j = pi[j-1]で戻る。 - 一致したら
j += 1。 j == Mになったらマッチ完成。
- 同様に不一致時は
- マッチ終了位置が
i(0-index)なので、開始位置はi-M+1(0-index)。
問題は 1-index で答えるのでi-M+2を出力して終了。 - 最後まで見つからなければ
-1。
計算量
- 時間計算量: \(O(N+M)\)
- 空間計算量: \(O(M)\)
実装のポイント
KMP は「不一致時に
while j > 0 and ...で戻す」部分が重要です。見つかった位置の 1-index 変換に注意:
- 0-index 開始位置
i-M+1 - 1-index 開始位置
i-M+2
- 0-index 開始位置
この実装は最初に見つかった時点で
returnするので、最小の \(i\) を正しく出力できます。ソースコード
import sys
def main():
input = sys.stdin.readline
N, M = map(int, input().split())
A = list(map(int, input().split()))
B = list(map(int, input().split()))
# KMP prefix function for pattern B
pi = [0] * M
j = 0
for i in range(1, M):
while j > 0 and B[i] != B[j]:
j = pi[j - 1]
if B[i] == B[j]:
j += 1
pi[i] = j
# KMP search in A
j = 0
for i in range(N):
while j > 0 and A[i] != B[j]:
j = pi[j - 1]
if A[i] == B[j]:
j += 1
if j == M:
# match ends at i (0-indexed), start is i-M+1
print(i - M + 2) # convert to 1-indexed
return
print(-1)
if __name__ == "__main__":
main()
この解説は gpt-5.3-codex によって生成されました。