อืมฉันสามารถนึกถึงอัลกอริธึมที่เป็นไปได้สองอย่าง: การสแกนเชิงเส้นผ่านลำดับAหรือสร้างพจนานุกรมด้วยการค้นหาดัชนีเวลาคงที่
หากคุณกำลังทดสอบการเรียงลำดับที่อาจเกิดขึ้นBกับลำดับA ที่มีขนาดใหญ่กว่านี้ฉันขอแนะนำให้คุณใช้ตัวแปรกับพจนานุกรม
การสแกนเชิงเส้น
ลักษณะ
เรายังคงเคอร์เซอร์สำหรับลำดับ จากนั้นเราก็ย้ำผ่านรายการทั้งหมดใน subsequence B สำหรับแต่ละรายการเราเลื่อนเคอร์เซอร์ไปข้างหน้าในAจนกระทั่งพบรายการที่ตรงกัน หากไม่พบรายการที่ตรงกันBนั้นจะไม่เป็นลำดับ
สิ่งนี้จะทำงานในO (seq.size)เสมอ
pseudocode
สไตล์ที่จำเป็น:
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
ตัวอย่างการนำไปใช้ (Perl):
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;
ค้นหาพจนานุกรม
ลักษณะ
เราแมปรายการของลำดับAกับดัชนีของพวกเขา จากนั้นเราจะค้นหาดัชนีที่เหมาะสมสำหรับแต่ละรายการในBข้ามดัชนีที่มีขนาดเล็กและเลือกดัชนีที่เล็กที่สุดเท่าที่จะเป็นไปได้ เมื่อไม่พบดัชนีดังนั้นBจะไม่เป็นลำดับ
ทำงานในสิ่งที่ต้องการO (subseq.size · k)ที่kseq
อธิบายวิธีจำนวนตัวเลขที่ซ้ำกันที่มีอยู่ใน บวกค่าใช้จ่ายO (seq.size)
ข้อได้เปรียบของการแก้ปัญหานี้คือการตัดสินใจเชิงลบสามารถเข้าถึงได้เร็วขึ้น (ลงถึงเวลาคงที่) เมื่อคุณชำระค่าใช้จ่ายในการสร้างตารางการค้นหาแล้ว
pseudocode:
สไตล์ที่จำเป็น:
# 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
ตัวอย่างการนำไปใช้ (Perl):
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 (subseq.size) ได้หากเราแลกเปลี่ยนในหน่วยความจำมากขึ้น แทนที่จะทำแผนที่องค์ประกอบไปยังดัชนีของพวกเขาเราสร้างกราฟที่แต่ละโหนดแสดงองค์ประกอบที่ดัชนี ขอบแสดงเปลี่ยนไปได้เช่นลำดับจะมีขอบa, b, a
a@1 → b@2, a@1 → a@3, b@2 → a@3
กราฟนี้เทียบเท่ากับเครื่องสถานะ จำกัด
ในระหว่างการค้นหาเรารักษาเคอร์เซอร์ซึ่งเริ่มต้นเป็นโหนดแรกของทรี จากนั้นเราเดินขอบสำหรับองค์ประกอบในรายการย่อยแต่ละB หากไม่มีขอบดังกล่าวแสดงว่าBไม่ใช่รายการย่อย หากหลังจากองค์ประกอบทั้งหมดเคอร์เซอร์มีโหนดที่ถูกต้องแล้วBเป็นรายการย่อย
pseudocode
สไตล์ที่จำเป็น:
# 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
ตัวอย่างการนำไปใช้ (Perl):
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;