हम्म, मैं दो संभावित एल्गोरिदम के बारे में सोच सकता हूं: ए अनुक्रम के माध्यम से एक रैखिक स्कैन , या सूचकांकों के निरंतर-समय की खोज के साथ एक शब्दकोश का निर्माण।
यदि आप एक बड़े अनुक्रम A के विरुद्ध कई संभावित अनुवर्ती B का परीक्षण कर रहे हैं , तो मेरा सुझाव है कि आप शब्दकोश के साथ संस्करण का उपयोग करें।
रैखिक स्कैन
विवरण
हम अनुक्रम ए के लिए एक कर्सर बनाए रखते हैं । फिर हम बाद के बी में सभी वस्तुओं के माध्यम से पुनरावृति करते हैं । प्रत्येक आइटम के लिए, हम ए को कर्सर को तब तक आगे बढ़ाते हैं जब तक हमें एक मिलान आइटम नहीं मिल जाता है। यदि कोई मिलान आइटम नहीं मिला, तो B एक बाद का नहीं है।
यह हमेशा O (seq.size) में चलता है ।
स्यूडोकोड
शाही शैली:
def subsequence? seq, subseq:
i = 0
for item in subseq:
i++ while i < seq.size and item != seq[i]
return false if i == seq.size
return true
कार्यात्मक शैली:
let rec subsequence? = function
| _ [] -> true
| [] _ -> false
| cursor::seq item::subseq ->
if cursor = item
then subsequence? seq subseq
else subsequence? seq item::subseq
उदाहरण कार्यान्वयन (पर्ल):
use strict; use warnings; use signatures; use Test::More;
sub is_subsequence_i ($seq, $subseq) {
my $i = 0;
for my $item (@$subseq) {
$i++ while $i < @$seq and $item != $seq->[$i];
return 0 if $i == @$seq;
}
return 1;
}
sub is_subsequence_f ($seq, $subseq) {
return 1 if @$subseq == 0;
return 0 if @$seq == 0;
my ($cursor, @seq) = @$seq;
my ($item, @subseq) = @$subseq;
return is_subsequence_f(\@seq, $cursor == $item ? \@subseq : $subseq);
}
my $A = [1, 2, 3, 4];
my $B = [1, 3];
my $C = [1, 3, 4];
my $D = [3, 1];
my $E = [1, 2, 5];
for my $is_subsequence (\&is_subsequence_i, \&is_subsequence_f) {
ok $is_subsequence->($A, $B), 'B in A';
ok $is_subsequence->($A, $C), 'C in A';
ok ! $is_subsequence->($A, $D), 'D not in A';
ok ! $is_subsequence->($A, $E), 'E not in A';
ok $is_subsequence->([1, 2, 3, 4, 3, 5, 6], [2, 3, 6]), 'multiple nums';
}
done_testing;
शब्दकोश खोज
विवरण
हम दृश्य के आइटम नक्शा एक उनके सूचकांकों करने के लिए। फिर हम बी में प्रत्येक आइटम के लिए उपयुक्त सूचक देखते हैं , उन सूचक को छोड़ दें जो छोटे हैं, और छोटी सीमा को कम सीमा के रूप में चुन सकते हैं। जब कोई संकेत नहीं मिलता है, तो बी एक बाद नहीं है।
O (subseq.size · k) जैसी किसी चीज़ में चलता है , जहाँ k बताता है कि वहाँ कितने डुप्लिकेट नंबर हैं seq
। साथ ही एक O (seq.size) ओवरहेड
इस समाधान का लाभ यह है कि एक नकारात्मक निर्णय बहुत तेजी से (निरंतर समय तक) तक पहुंचा जा सकता है, एक बार जब आप लुकअप तालिका बनाने के ओवरहेड का भुगतान कर चुके होते हैं।
स्यूडोकोड:
शाही शैली:
# preparing the lookup table
dict = {}
for i, x in seq:
if exists dict[x]:
dict[x].append(i)
else:
dict[x] = [i]
def subsequence? subseq:
min_index = -1
for x in subseq:
if indices = dict[x]:
suitable_indices = indices.filter(_ > min_index)
return false if suitable_indices.empty?
min_index = suitable_indices[0]
else:
return false
return true
कार्यात्मक शैली:
let subsequence? subseq =
let rec subseq-loop = function
| [] _ -> true
| x::subseq min-index ->
match (map (filter (_ > min-index)) data[x])
| None -> false
| Some([]) -> false
| Some(new-min::_) -> subseq-loop subseq new-min
in
subseq-loop subseq -1
उदाहरण कार्यान्वयन (पर्ल):
use strict; use warnings; use signatures; use Test::More;
sub build_dict ($seq) {
my %dict;
while (my ($i, $x) = each @$seq) {
push @{ $dict{$x} }, $i;
}
return \%dict;
}
sub is_subsequence_i ($seq, $subseq) {
my $min_index = -1;
my $dict = build_dict($seq);
for my $x (@$subseq) {
my $indices = $dict->{$x} or return 0;
($min_index) = grep { $_ > $min_index } @$indices or return 0;
}
return 1;
}
sub is_subsequence_f ($seq, $subseq) {
my $dict = build_dict($seq);
use feature 'current_sub';
return sub ($subseq, $min_index) {
return 1 if @$subseq == 0;
my ($x, @subseq) = @$subseq;
my ($new_min) = grep { $_ > $min_index } @{ $dict->{$x} // [] } or return 0;
__SUB__->(\@subseq, $new_min);
}->($subseq, -1);
}
my $A = [1, 2, 3, 4];
my $B = [1, 3];
my $C = [1, 3, 4];
my $D = [3, 1];
my $E = [1, 2, 5];
for my $is_subsequence (\&is_subsequence_i, \&is_subsequence_f) {
ok $is_subsequence->($A, $B), 'B in A';
ok $is_subsequence->($A, $C), 'C in A';
ok ! $is_subsequence->($A, $D), 'D not in A';
ok ! $is_subsequence->($A, $E), 'E not in A';
ok $is_subsequence->([1, 2, 3, 4, 3, 5, 6], [2, 3, 6]), 'multiple nums';
}
done_testing;
शब्दकोश लुकअप वेरिएंट: एक परिमित राज्य मशीन के रूप में एन्कोडिंग
विवरण
यदि हम अधिक मेमोरी में ट्रेड करते हैं तो हम एल्गोरिदम की जटिलता को घटाकर O (सबसेक्साइज़) तक कम कर सकते हैं । तत्वों को उनके सूचकांक में मैप करने के बजाय, हम एक ग्राफ बनाते हैं जहां प्रत्येक नोड अपने सूचकांक में एक तत्व का प्रतिनिधित्व करता है। किनारों को संभव संक्रमण दिखाते हैं, जैसे अनुक्रम a, b, a
में किनारे होंगे a@1 → b@2, a@1 → a@3, b@2 → a@3
। यह ग्राफ एक परिमित राज्य मशीन के बराबर है।
लुकअप के दौरान हम एक कर्सर रखते हैं जो शुरू में पेड़ का पहला नोड है। हम तब सबलिस्ट B में प्रत्येक तत्व के लिए किनारे चलते हैं । यदि ऐसी कोई धार मौजूद नहीं है, तो B कोई सबलिस्ट नहीं है। यदि सभी तत्वों के बाद कर्सर में एक वैध नोड होता है, तो बी एक सबलिस्ट है।
स्यूडोकोड
शाही शैली:
# preparing the graph
graph = {}
for x in seq.reverse:
next_graph = graph.clone
next_graph[x] = graph
graph = next_graph
def subseq? subseq:
cursor = graph
for x in subseq:
cursor = graph[x]
return false if graph == null
return true
कार्यात्मक शैली:
let subseq? subseq =
let rec subseq-loop = function
| [] _ -> true
| x::subseq graph -> match (graph[x])
| None -> false
| Some(next-graph) -> subseq-loop subseq next-graph
in
subseq-loop subseq graph
उदाहरण कार्यान्वयन (पर्ल):
use strict; use warnings; use signatures; use Test::More;
sub build_graph ($seq) {
my $graph = {};
for (reverse @$seq) {
$graph = { %$graph, $_ => $graph };
}
return $graph;
}
sub is_subsequence_i ($seq, $subseq) {
my $cursor = build_graph($seq);
for my $x (@$subseq) {
$cursor = $cursor->{$x} or return 0;
}
return 1;
}
sub is_subsequence_f ($seq, $subseq) {
my $graph = build_graph($seq);
use feature 'current_sub';
return sub ($subseq, $graph) {
return 1 if @$subseq == 0;
my ($x, @subseq) = @$subseq;
my $next_graph = $graph->{$x} or return 0;
__SUB__->(\@subseq, $next_graph);
}->($subseq, $graph);
}
my $A = [1, 2, 3, 4];
my $B = [1, 3];
my $C = [1, 3, 4];
my $D = [3, 1];
my $E = [1, 2, 5];
for my $is_subsequence (\&is_subsequence_i, \&is_subsequence_f) {
ok $is_subsequence->($A, $B), 'B in A';
ok $is_subsequence->($A, $C), 'C in A';
ok ! $is_subsequence->($A, $D), 'D not in A';
ok ! $is_subsequence->($A, $E), 'E not in A';
ok $is_subsequence->([1, 2, 3, 4, 3, 5, 6], [2, 3, 6]), 'multiple nums';
}
done_testing;