文字列照合アルゴリズム

Download Report

Transcript 文字列照合アルゴリズム

1
アルゴリズムとデータ
構造
第9回文字列照合アルゴリズム
2014/12/10
アルゴリズムとデータ構造 2014
2
文字列照合問題
文字列照合問題(String pattern matching problem)とは
テキスト文字列tの中にパターン文字列pが現れるかを判定し、
現れる場合にはその位置をみつける問題
※pがtの中に複数回現れる場合は、最初の位置のみをみつけるものとする。
以後、次の記号を用いる。
Σ:アルファベット
t: Σに含まれる文字からなる長さnの文字列t[0]t[1]・・・t[n-1]
テキスト
p: Σに含まれる文字からなる長さmの文字列p[0]p[1]・・・p[m-1] パターン
2014/12/10
アルゴリズムとデータ構造 2014
3
素朴なアルゴリズム
for(j=0;j<=n-m;j++) {
for(i=0;i<m;i++) {
if(t[j+i]!=p[i]) break;
}
if(i>=m) return j; /* found at position j */
}
return -1 /* not found */
j
0
[素朴なアルゴリズム]
j+i
n-1
t
p
0
i m-1
最悪時間計算量 O(mn)
最悪時間計算量がO(m+n)のアルゴリズム
Knuth-Morris-Pratt (KMP)法
Boyer-Moore (BM)法
2014/12/10
実用性からKMP法より優れている
アルゴリズムとデータ構造 2014
4
Knuth-Morris-Pratt法
[考え方] パターンpを前から照合してp[i]で失敗したとき、
その前のi文字は一致しているという情報を有効に使う
j
0
n-1
t
Donald Knuth
p
1938年生
Turing Award, 1974
0
i
0
f(i)
m-1
アルゴリズムに関する著作
The art of Computer Programming
シリーズは有名。Texの開発者。
m-1
これだけずらして、パターンをf(i)文字目からチェックすればよい
f(i)は失敗関数と呼び、次のように定義される。
f(i)=max{u: p[0]p[1]・・・p[u-1]=p[i-u]p[i-u+1]・・・p[i-1], u<i} (i=1,2,…,m-1)
パターンpにおいて先頭からのu文字と、位置i直前のu文字が一致するような最大のu(<i)
2014/12/10
アルゴリズムとデータ構造 2014
5
KMPアルゴリズム
[KMPアルゴリズム]
i=0;j=0;
compf(); /* fの計算
while((i<m)&&(j<n))
if(p[i]==t[j]) {
i++;j++;
}
else {
if(i==0) j++;
else
i=f[i];
}
}
If(i>=m) return j-m;
else
return -1;
2014/12/10
*/
{
/* found at position j-m */
/* not found */
compf()
{
i=0;j=1;
f[1]=0;
while(j<m) {
if(p[i]==p[j]) {
f[++j]=++i;
}
else {
if(i==0) f[++j]=0;
else
i=f[i];
}
}
}
アルゴリズムとデータ構造 2014
6
compfの動作
・p[j]=p[i]の場合
p
f[i] (i≦j)の値からf[j+1] (f[++j])の値を計算
j
△
p
・p[j]≠p[i]の場合
p
f[j+1]=f[j]+1=i+1 (f[++j]=++i)
△
i ← f[j]と等しくなるように設定されている。
j
△
p
○
i
□
f[i]
f[f[i]]
△
○
f[f[i]]
f[j+1]=f[f[i]]+1
2014/12/10
□
f[f[f[i]]]=0
f[j+1]=0
アルゴリズムとデータ構造 2014
7
KMP法の最悪時間計算量はO(n+m)
(証明)
まず、compfを除いた時間計算量を求める。
ループの中は定数ステップで抑えられるので、ループの回数をもとめればよい。
ループの中では、
(1) iとjが1ずつ増加
(2) jだけ1増加
(3) iだけ減少
のいずれかが起こる。
j<nなので(1),(2)は合わせてn回しか起こらない。
したがってiの増加も高々n回であり、(3)での減少も高々n回しか起こらない。
したがってループの回数は高々2n回で抑えられる。
よって時間計算量はO(n)である。
compfも基本的に同じなのでO(m)で抑えられる。
したがって、全体ではO(n+m)となる。
2014/12/10
アルゴリズムとデータ構造 2014
8
Boyer-Moore法
[考え方] パターンを後ろから照合し、次の2つの工夫を行う。
1. 照合に失敗した文字t[j]がパターンpにマッチするようにずらす。
j
t
p
j
j+m-s-1
△
□
△
○
○
s
i
i
一番右の△の位置→
△
j+m
○
d[△]=m-s-1
○
m-s-1
d[□]=m
(□はpに現れない文字)
2. 照合済の部分がマッチするようにpをずらす。(失敗関数によるずらし)
j
j
j+m-i+s-1
j+m-i+s-1
t
△
□
s
p
□
i-s
○
2014/12/10
○
m-i-1
s
s
i
□
dd[i]=m-i+s-1
s
i
○
○
dd[i]=m-i+s-1
m-i-1
アルゴリズムとデータ構造 2014
9
BMアルゴリズム
[BMアルゴリズム]
i=m-1;j=m-1;
compd(); /* dの計算 */
compdd() /* ddの計算 */
while((i>=0)&&(j<n)) {
if(p[i]==t[j]) {
i--;j--;
}
else {
j=j+max(d[t[j]],dd[i]);
i=m-1;
}
}
If(i<0) return j+1; /* found at position j+1 */
else
return -1; /* not found */
#define MAXCODE 256 /* 文字コードの最大値
compd()
{
for(i=0;i<MAXCODE;i++) d[i]=m;
for(i=0;i<m;i++) d[p[i]]=m-i-1;
}
2014/12/10
1バイト文字を仮定。ASCII
コードだと思えばよい。
pに出現しない文字
に対してはmのまま
pに出現する文字はpに
おけるその文字の最後
の出現位置iの後ろの文
字数m-i-1に上書き
アルゴリズムとデータ構造 2014
10
ddの計算
dd[i]の計算は、KMP法で使った失敗関数fの後方一致版gを使って計算できる。
g[j]=min{i: p[j+1]p[j+2]・・・p[j+(m-1)-i]=p[i+1]p[i+2]・・・p[m-1]} (j=-1,0,…,m-2)
gの計算はfの計算と同様に行うことができる。
compg()
{
i=m-1;j=m-2;
g[m-2]=m-1;
while(j>=0) {
if(p[i]==p[j]) {
g[--j]=--i;
}
else {
if(i==m-1) g[--j]=m-1;
else
i=g[i];
}
}
}
2014/12/10
パターンpにおいて位置iの直後から最
後尾までの文字列と位置jの直後の文
字列が一致するような最小のi(>j)
j
p
g[j]
-1
p
g[-1]
アルゴリズムとデータ構造 2014
11
ddの計算(続き)
ddはcompgで求めたgから計算できる。
(m-1) – max{j:g[j]=i,p[i]≠p[j]}
j1
p
(g[j]=iかつp[i]≠p[j]となるjが存在する場合)
j2
△
j3
△
i=g[j1]=g[j2]=g[j3]
△
○
dd[i]
m + (min{gn[-1]:gn[-1]≧i,n=1,2,…} – i)
(そうでない場合)
ただし、
dd[i]=
gn(-1)=g[g[…g[-1]…]
とする。
-1
n回
i1
g[-1] i2 g[g[-1]]
p
m
g[-1]-i1
m
2014/12/10
g[g[-1]]-i2
アルゴリズムとデータ構造 2014
12
compddのアルゴリズム
compdd()
{
compg();
for(i=0;i<m;i++) dd[i]=MAX_INT;
for(i=0;i<m-1;i++) {
if(p[i]!=p[g[i]]) dd[g[i]]=(m-1)-i;
}
i=0;
g[m-1]=m;
for(j=g[-1];j<m;j=g[j]) {
for(;i<=j;i++) {
if(m+j-i<dd[i]) dd[i]=m+j-i;
}
}
}
2014/12/10
int型変数に格納できる値の最大値。Cの標
準ライブラリのヘッダーファイルlimit.hに定
義されている。通常は2137483647(231-1)。
ここではddの取り得る値の最大値よりおお
きければ何でもよい。
g[i]の値が同じになるものは最後のiで上書
きされる。
BM法の最悪時間計算量 O(n+m)
(証明)
compd(), compdd()はO(m)である。
その後のwhile文のループ回数は4n回
で抑えられることが知られている。
よって全体ではO(m+n)である。
(証明終り)
アルゴリズムとデータ構造 2014