F: Longest Match

Download Report

Transcript F: Longest Match

F: Longest Match
原案: 鮫島
担当: 鮫島,安藤
問題概要

文字列 𝑆 と,𝑚 個のクエリが与えられる

各クエリについて,文字列 𝑥𝑖 , 𝑦𝑖 が与えられる

𝑆の部分文字列で,𝑥𝑖 で始まり𝑦𝑖 で終わるものの中
で,最長の文字列の長さは?
 そのような部分文字列がない場合,0を出力

制約

𝑆 ≤ 2 ∗ 105 , 𝑚 ≤ 105 ,
𝑥𝑖 + 𝑦𝑖 ≤ 2 ∗ 105
例1
S = “abracadabra”
 クエリ

 𝑥𝑖
= “ab”, 𝑦𝑖 = “a”
 “ab”から始まって,”a”で終わる最長の部分文字列
 “abracadabra”
が最長なので,11が答え
例2
S = “abracadabra”
 クエリ

 𝑥𝑖
= “b”, 𝑦𝑖 = “c”
 “b”から始まって,”c”で終わる最長の部分文字列
 “brac”
が最長なので,4が答え
例3
S = “abracadabra”
 クエリ

 𝑥𝑖
= “z”, 𝑦𝑖 = “z”
 “z”から始まって,”z”で終わる最長の部分文字列
 そのような部分文字列は存在しないので,0が答え
最長になる部分文字列?
𝑆の中で
 𝑥𝑖 が最初に見つかる位置
𝑎𝑖
 𝑦𝑖 が最後に見つかる位置
𝑏𝑖
が求められれば,最長の部分文字列も求められそう
𝑥𝑖 , 𝑦𝑖 に対してそれぞれ独立な問題を解けばよい
𝑆 = “abracadabra”
𝑥𝑖 = “b”, 𝑦𝑖 = “c” なら
𝑥𝑖 が最初に見つかる位置は, “abracadabra”
• 𝑎𝑖 = 1
• “abracadabra”, 𝑎𝑖 = 8 は最初じゃないのでだめ
𝑦𝑖 が最後に見つかる位置は, “abracadabra”
• 𝑏𝑖 = 4
よって,最長の部分文字列の長さは (𝑎𝑖 – 𝑏𝑖 ) + 1 = 4
愚直な解法

文字列 𝑆 から文字列 𝑇 が最初に見つかる位置を求める
(文字列𝑆, 𝑇を反転すれば,最後にみつかる位置も求められる)

考えられるアルゴリズム
 brute-force
 1クエリ当たり
O(|S||T|)
 間に合わない
 KMP法,BM法

𝑇 ≤ 𝑆 なので,1クエリ当たり O(|S|)
 クエリ数が膨大なので,これでも間に合わない
解法1

suffix array(接尾辞配列) を使う
 接尾辞
: 後ろについている文字列
 接尾辞配列
 長さ
𝑆
n の文字列 𝑆 について
0: 𝑛 , 𝑆 1: 𝑛 , 𝑆 2: 𝑛 … 𝑆[𝑛 − 1: 𝑛]を作成
11 a
8 abra
1 abracadabra
4 acadabra
6 adabra

𝑆 𝑎: 𝑏 = 𝑆 𝑎 + 𝑆 𝑎 + 1 + … + 𝑆[𝑏 − 1]
9 bra

要するに部分文字列
2 bracadabra
 これらを辞書順にsortしたもの

開始位置 接尾辞
構築方法は色々
 蟻本ではO(
𝑆 log S
5 cadabra
7 dabra
10 ra
2
) が紹介
3 racadbra
解法1

何が嬉しいのか

suffix array を構築すると,文字列𝑇が
現れる位置を O( T log |𝑆|) で求められる!
 suffix
array に対して2分探索
 2分探索部分でバグらせない様に注意
開始位置 接尾辞
11 a
8 abra
1 abracadabra
4 acadabra
6 adabra
例) 𝑇 = “ab“ の時
右の表のオレンジの部分が,
”ab”の見つかる場所となる
9 bra
2 bracadabra
5 cadabra
7 dabra
10 ra
3 racadbra
解法1
オレンジの部分で,最小の開始位置を求めたい
segment tree を使う
開始位置 接尾辞
11 a
 構築はO(|𝑆|)

8 abra
sparse tableでもOK
1 abracadabra
4 acadabra
 メモリ上限に注意
 構築は

6 adabra
O( 𝑆 log 𝑆 )
1回のクエリをO(log 𝑆 )で処理
O(𝐿 log |𝑆| + 𝑆 log 𝑆
2 bracadabra
5 cadabra
クエリ文字列の合計を L とすると
2)
9 bra
で間に合う
7 dabra
10 ra
3 racadbra
解法2

Aho-Corasickを使う
 非想定解でした

クエリ文字列を先読みし,検索パターンとして
Aho-Corasickを構築する

作成したtrie木上で,文字列|𝑆|を探索する
 最初にクエリ文字列の終端と一致したindexを保存
クエリ文字列の合計を L とすると
構築 : O(𝐿) , クエリ処理 : O(𝐿 + 𝑆 )
所感

そこそこバグらせている人が多かった
 suffix

array に対する2分探索がバグりやすい
想像以上のsubmit,AC
 夏合宿に来るレベルの人には典型だった?
 ”abracadabra”を見てsuffix
arrayを考えた人も
ジャッジ解

鮫島: 142行 3141bytes
 ヘッダを除く

安藤: 210行 5231bytes
Results

FA: Navi (52:56)

25ACs, 28 trying teams, 66 submissions