Biorąc pod uwagę n ciągów, czy jeden z nich jest podciągiem innego?

9

Załóżmy, że otrzymaliśmy zbiór ciągów, . Chciałbym wiedzieć, czy którykolwiek z tych ciągów jest podciągiem dowolnego innego ciągu w kolekcji. Innymi słowy, chciałbym algorytm dla następującego zadania:nS1,,Sn

Dane wejściowe:S1,,Sn

Wyjście: takie, że jest podłańcuchem i , lub None, jeśli nie takie existi,jSiSjiji,j

Czy istnieje na to wydajny algorytm?

Jeśli zamienimy „podłańcuch” na „przedrostek”, istnieje skuteczny algorytm (posortuj ciągi, a następnie wykonaj skanowanie liniowe, aby porównać sąsiednie ciągi; sortowanie zapewni, że podciągi są sąsiadujące). Ale trudniejsze wydaje się sprawdzenie, czy dowolny ciąg znaków jest podciągiem dowolnego innego ciągu. Naiwny algorytm polega na iteracji wszystkich par , ale wymaga to testów podciągowych . Czy istnieje bardziej wydajny algorytm?i,jΘ(n2)

Myślę, że moglibyśmy nazwać to „testowaniem podciągów na wszystkich parach” lub coś w tym rodzaju.

Moim ostatecznym celem jest przycięcie kolekcji, aby żaden ciąg nie był podciągiem innych elementów, poprzez usunięcie każdego z nich, który jest podciągiem czegoś innego w kolekcji.

DW
źródło
Wskazówka: tablica sufiksów.
pseudonim
Na marginesie, nie jest poprawne, jeśli usuniesz podciągi, gdy je znajdziesz. Będzie mniej. Ponadto należy sortować według długości, ponieważ dłuższy ciąg nie może pojawić się w krótszym ciągu. Znowu jest tutaj źle. Θ(n2)Θ(n2)
Alexis Wilke,
@AlexisWilke, jest poprawna: taka jest liczba testów podłańcuchowych w najgorszym przypadku (najgorszy przypadek to taki, że żaden ciąg nie jest podciągiem innym). Sortowanie według długości daje tylko współczynnik dwa, co nie wpływa na asymptotyki. Θ(n2)
DW

Odpowiedzi:

6

Możesz zbudować drzewo sufiksów w czasie liniowym i sprawdzić, czy istnieje wewnętrzny węzeł, który odpowiada pełnemu ciągowi (stały czas na węzeł).

Bardziej szczegółowo załóżmy, że otrzymujemy ciągi .s1,,snΣ

  1. Budowanie (uogólnionego) drzewa sufiks z o parami różne znaczniki zaciskowe .s1$1,s2$2,,sn$nn$1,,$nΣ

    Za pomocą algorytmu Ukkonen można to zrobić w czasie liniowym; liniowy w sumie wszystkich długości łańcucha.

  2. Zakładając, że etykieta liście z , jeżeli stanowią one przyrostek z , przemierzać drzewo i znaleźć te liście oznaczonych , czyli liści, które odpowiadają w pełni smyczki.(i,j)si[j..|si|]sin(i,0)

    To zajmuje czas liniowy w rozmiarze drzewa, który sam jest liniowy w rozmiarze wejściowym.

  3. Liście potomne elementu nadrzędnego (do którego prowadzi krawędź oznaczona ) reprezentują wszystkie dopasowania z zestawu; wynika to z podstawowego niezmiennika drzewek sufiksów. Znajdź dowolne dopasowanie, schodząc do dowolnego liścia (ale ).(i,0)$i(i,0)

    To znowu zajmuje czas liniowy.

Oddzielne znaczniki końcowe nie są tak naprawdę konieczne; jeden używany do zakończenia wszystkich ciągów jest wystarczający, o ile pozwala się na wiele etykiet na liść.

Raphael
źródło