これは、通常* O(N) で実行され、O(m) スペースを取る実装です。ここで、m は長さ (S) です。
これは、測量士の鎖のアイデアを使用しています
。長さ k の鎖でつながれた一連の極を想像してください。弦の最初に最初のポールを固定します。一致する文字が見つかるまで、次の極を進めます。そのポールを置きます。たるみがある場合は、次の文字に進みます。それ以外の場合は、前の極が前方にドラッグされているため、戻って次に近い一致に移動する必要があります。最後に到達するか、たるみがなくなるまで繰り返します。
typedef struct chain_t{
int slack;
int pole;
} chainlink;
int subsequence_k_impl(char* t, char* s, int k, chainlink* link, int len)
{
char* match=s;
int extra = k; //total slack in the chain
//for all chars to match, including final null
while (match<=s+len){
//advance until we find spot for this post or run out of chain
while (t[link->pole] && t[link->pole]!=*match ){
link->pole++; link->slack--;
if (--extra<0) return 0; //no more slack, can't do it.
}
//if we ran out of ground, it's no good
if (t[link->pole] != *match) return 0;
//if this link has slack, go to next pole
if (link->slack>=0) {
link++; match++;
//if next pole was already placed,
while (link[-1].pole < link->pole) {
//recalc slack and advance again
extra += link->slack = k-(link->pole-link[-1].pole-1);
link++; match++;
}
//if not done
if (match<=s+len){
//currrent pole is out of order (or unplaced), move it next to prev one
link->pole = link[-1].pole+1;
extra+= link->slack = k;
}
}
//else drag the previous pole forward to the limit of the chain.
else if (match>=s) {
int drag = (link->pole - link[-1].pole -1)- k;
link--;match--;
link->pole+=drag;
link->slack-=drag;
}
}
//all poles planted. good match
return 1;
}
int subsequence_k(char* t, char* s, int k)
{
int l = strlen(s);
if (strlen(t)>(l+1)*(k+1))
return -1; //easy exit
else {
chainlink* chain = calloc(sizeof(chainlink),l+2);
chain[0].pole=-1; //first pole is anchored before the string
chain[0].slack=0;
chain[1].pole=0; //start searching at first char
chain[1].slack=k;
l = subsequence_k_impl(t,s,k,chain+1,l);
l=l?chain[1].pole:-1; //pos of first match or -1;
free(chain);
}
return l;
}
※big-Oはよくわかりません。最初はO(km+N)のようなものだと思っていました。テストでは、一致が良好な場合は平均 2N 未満であり、一致しなかった場合は N 未満です。
...しかし.. 奇妙な縮退のケースがあります。サイズ のアルファベットから選択されたランダムな文字列のA
場合、k = 2A+1
. この場合でも O(Nm) よりも優れており、k を少し増減すると O(N) に戻ります。 Gist誰かが興味を持っている場合は、ここを参照してください。