Rozważmy następującą tablicę:
/www/htdocs/1/sites/lib/abcdedd
/www/htdocs/1/sites/conf/xyz
/www/htdocs/1/sites/conf/abc/def
/www/htdocs/1/sites/htdocs/xyz
/www/htdocs/1/sites/lib2/abcdedd
jaki jest najkrótszy i najbardziej elegancki sposób wykrywania wspólnej ścieżki bazowej - w tym przypadku
/www/htdocs/1/sites/
i usuwając go ze wszystkich elementów tablicy?
lib/abcdedd
conf/xyz
conf/abc/def
htdocs/xyz
lib2/abcdedd
Odpowiedzi:
Napisz funkcję,
longest_common_prefix
która pobiera dwa ciągi jako dane wejściowe. Następnie zastosuj go do łańcuchów w dowolnej kolejności, aby zredukować je do ich wspólnego przedrostka. Ponieważ jest skojarzona i przemienna, kolejność nie ma znaczenia dla wyniku.Jest to to samo, co w przypadku innych operacji binarnych, takich jak na przykład dodawanie lub największy wspólny dzielnik.
źródło
Załaduj je do trie struktury danych. Zaczynając od węzła nadrzędnego, zobacz, który z elementów potomnych jest większy niż jeden. Gdy znajdziesz ten magiczny węzeł, po prostu zdemontuj strukturę węzła macierzystego i ustaw bieżący węzeł jako główny.
źródło
źródło
/usr/lib
i/usr/lib2
podał/usr/lib
jako najdłuższą wspólną ścieżkę, a nie/usr/
). (Mam nadzieję) naprawiłem oba.Cóż, biorąc pod uwagę, że możesz użyć
XOR
w tej sytuacji, aby znaleźć wspólne części ciągu. Za każdym razem, gdy xorujesz dwa takie same bajty, jako wyjście otrzymasz zerowy bajt. Więc możemy to wykorzystać na naszą korzyść:Po tej pojedynczej pętli
$length
zmienna będzie równa najdłuższej wspólnej części bazowej między tablicą ciągów. Następnie możemy wyodrębnić część wspólną z pierwszego elementu:I masz to. Jako funkcja:
Zwróć uwagę, że używa więcej niż jednej iteracji, ale te iteracje są wykonywane w bibliotekach, więc w językach interpretowanych będzie to miało ogromny wzrost wydajności ...
Teraz, jeśli chcesz tylko pełnych ścieżek, musimy skrócić do ostatniego
/
znaku. Więc:Teraz może nadmiernie przeciąć dwie struny, takie jak
/foo/bar
i/foo/bar/baz
zostanie przycięte/foo
. Ale brakuje dodając kolejną rundę iteracji, aby ustalić, czy następny znak jest albo/
czy końcówki łańcucha, nie widzę sposób wokół, że ...źródło
Naiwnym podejściem byłoby eksplodowanie ścieżek
/
i sukcesywne porównywanie każdego elementu w tablicach. Czyli np. Pierwszy element byłby pusty we wszystkich tablicach, więc zostanie usunięty, następny element będziewww
taki sam we wszystkich tablicach, więc zostanie usunięty itp.Coś jak (
niesprawdzone)Następnie wystarczy ponownie implodować elementy
$exploded_paths
:Co daje mi:
To może nie być dobrze skalowane;)
źródło
Ok, nie jestem pewien, czy to jest kuloodporne, ale myślę, że działa:
Spowoduje to pobranie pierwszej wartości z tablicy jako łańcucha odniesienia. Następnie przeprowadzi iterację po ciągu referencyjnym i porówna każdy znak ze znakiem drugiego łańcucha w tej samej pozycji. Jeśli znak nie pasuje, ciąg referencyjny zostanie skrócony do pozycji znaku i porównany zostanie następny ciąg. Funkcja zwróci wówczas najkrótszy pasujący ciąg.
Wydajność zależy od podanych strun. Im wcześniej ciąg referencyjny zostanie skrócony, tym szybciej zakończy się kod. Naprawdę nie mam pojęcia, jak to ująć w formule.
Odkryłem, że podejście Artefacto do sortowania strun zwiększa wydajność. Dodawanie
przed
array_reduce
znacznie zwiększy wydajność.Zwróć również uwagę, że zwróci to najdłuższy pasujący podciąg początkowy , który jest bardziej wszechstronny, ale nie daje wspólnej ścieżki . Musisz biec
na wynik. Następnie możesz użyć wyniku, aby usunąć wartości
co powinno dać:
Opinie mile widziane.
źródło
Najszybciej możesz usunąć prefiks, czytając każdy znak tylko raz:
źródło
Ma to dużą zaletę, ponieważ nie ma liniowej złożoności czasowej; jednak w większości przypadków operacja ta na pewno nie zajmie więcej czasu.
Zasadniczo sprytną częścią (przynajmniej nie mogłem znaleźć w tym żadnej usterki) tutaj jest to, że po sortowaniu będziesz musiał tylko porównać pierwszą ścieżkę z ostatnią.
źródło
EDYTUJ Wariant mojej oryginalnej metody wykorzystujący array_walk do odbudowania tablicy
EDYTOWAĆ
Najbardziej wydajna i elegancka odpowiedź będzie prawdopodobnie polegać na przejęciu funkcji i metod z każdej z udzielonych odpowiedzi
źródło
Chciałbym
explode
wartości oparte na /, a następnie użyćarray_intersect_assoc
do wykrycia wspólnych elementów i zapewnienia, że mają prawidłowy odpowiedni indeks w tablicy. Powstała macierz może zostać ponownie połączona w celu utworzenia wspólnej ścieżki.Nie jest to testowane, ale idea polega na tym, że
$commonPath
tablica zawsze zawiera tylko elementy ścieżki, które zostały zawarte we wszystkich tablicach ścieżek, które zostały z nią porównane. Kiedy pętla jest kompletna, po prostu łączymy ją ponownie z /, aby uzyskać prawdę$commonPath
Aktualizacja Jak wskazał Felix Kling,
array_intersect
nie będzie rozważać ścieżek, które mają wspólne elementy, ale w innej kolejności ... Aby rozwiązać ten problem, użyłemarray_intersect_assoc
zamiastarray_intersect
Aktualizacja Dodano kod usuwający wspólną ścieżkę (lub tetris it!) Z tablicy.
źródło
/a/b/c/d
i/d/c/b/a
. Te same elementy, różne ścieżki.Problem można uprościć, patrząc tylko pod kątem porównania ciągów. Jest to prawdopodobnie szybsze niż dzielenie tablicy:
źródło
Może przeniesienie algorytmu używanego przez Pythona
os.path.commonprefix(m)
zadziała?To znaczy ... coś w stylu
Następnie możesz po prostu wstawić każdy element oryginalnej listy z długością wspólnego przedrostka jako przesunięciem początkowym.
źródło
Rzucę swój kapelusz na ring…
Stosowanie:
źródło
Cóż, są już tutaj rozwiązania, ale tylko dlatego, że było fajnie:
Wynik:
źródło
To działa dobrze ... podobnie do mark baker, ale używa str_replace
źródło
Prawdopodobnie zbyt naiwny i niedbały, ale działa. Użyłem tego algorytmu :
Wynik:
:)
źródło
/www/htdocs/1/sites/conf/
jako wspólne dopasowanie. Ponadto algorytm wyszukuje podciągi zaczynające się w dowolnym miejscu w ciągu, ale w przypadku tego pytania wiesz, że możesz zacząć od lokalizacji 0, co znacznie upraszcza.