Hmm, tôi có thể nghĩ về hai thuật toán có thể: Quét tuyến tính thông qua chuỗi A hoặc xây dựng một từ điển với tra cứu liên tục các chỉ số.
Nếu bạn đang thử nghiệm nhiều chuỗi con tiềm năng B dựa trên một chuỗi A lớn hơn , tôi khuyên bạn nên sử dụng biến thể với từ điển.
Quét tuyến tính
Sự miêu tả
Chúng tôi duy trì một con trỏ cho chuỗi Một . Sau đó, chúng ta lặp qua tất cả các mục trong dãy B . Đối với mỗi mục, chúng tôi di chuyển con trỏ về phía trước trong A cho đến khi chúng tôi tìm thấy một mục phù hợp. Nếu không tìm thấy mục phù hợp, thì B không phải là phần tiếp theo.
Điều này luôn chạy trong O (seq.size) .
Mã giả
Phong cách bắt buộc:
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
Phong cách chức năng:
let rec subsequence? = function
| _ [] -> true
| [] _ -> false
| cursor::seq item::subseq ->
if cursor = item
then subsequence? seq subseq
else subsequence? seq item::subseq
Ví dụ thực hiện (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;
Tra cứu từ điển
Sự miêu tả
Chúng tôi ánh xạ các mục của chuỗi A đến các chỉ số của chúng. Sau đó, chúng tôi tìm kiếm các chỉ số phù hợp cho từng mục trong B , bỏ qua các chỉ số nhỏ và chọn chỉ số nhỏ nhất có thể làm giới hạn thấp hơn. Khi không tìm thấy chỉ số nào, thì B không phải là một chuỗi con.
Chạy trong một cái gì đó như O (subseq.size · k) , trong đó k mô tả có bao nhiêu số trùng lặp seq
. Cộng với chi phí O (seq.size)
Ưu điểm của giải pháp này là một quyết định tiêu cực có thể đạt được nhanh hơn nhiều (xuống đến thời gian không đổi), một khi bạn đã trả chi phí xây dựng bảng tra cứu.
Mã giả:
Phong cách bắt buộc:
# 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
Phong cách chức năng:
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
Ví dụ thực hiện (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;
Biến thể tra cứu từ điển: Mã hóa dưới dạng máy trạng thái hữu hạn
Sự miêu tả
Chúng ta có thể giảm độ phức tạp thuật toán xuống còn O (subseq.size) nếu chúng ta giao dịch trong nhiều bộ nhớ hơn. Thay vì ánh xạ các phần tử vào các chỉ mục của chúng, chúng ta tạo một biểu đồ trong đó mỗi nút đại diện cho một phần tử tại chỉ mục của nó. Các cạnh hiển thị các chuyển tiếp có thể, ví dụ như chuỗi a, b, a
sẽ có các cạnh a@1 → b@2, a@1 → a@3, b@2 → a@3
. Biểu đồ này tương đương với một máy trạng thái hữu hạn.
Trong quá trình tra cứu, chúng tôi duy trì một con trỏ ban đầu là nút đầu tiên của cây. Sau đó chúng tôi đi cạnh cho mỗi phần tử trong sublist B . Nếu không có cạnh như vậy tồn tại, thì B không có danh sách con. Nếu sau tất cả các phần tử con trỏ chứa một nút hợp lệ, thì B là một danh sách con.
Mã giả
Phong cách bắt buộc:
# 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
Phong cách chức năng:
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
Ví dụ thực hiện (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;