Jakiego rodzaju są moje sufiksy?

10

Wprowadzenie

Więc marnuję swój czas, ponownie badając algorytmy sortowania sufiksów, oceniając nowe pomysły ręcznie i w kodzie. Ale zawsze staram się zapamiętać rodzaj moich sufiksów! Czy możesz mi powiedzieć, jakiego typu są moje sufiksy?

Najbardziej lewe co?

Wiele algorytmów sortowania sufiksów (SAIS, KA, mój własny daware) grupuje sufiksy do różnych typów w celu ich posortowania. Istnieją dwa podstawowe typy: typu S i L typu przyrostki. Sufiksy typu S to sufiksy, które są leksykograficznie mniejsze ( S maller) niż następujący sufiks i typ L, jeśli jest leksykograficznie większy ( L arger). Najbardziej lewy typ S ( typ LMS ) to po prostu: sufiks typu S poprzedzony sufiksem typu L.

Cechą szczególną tych sufiksów typu LMS jest to, że po ich posortowaniu możemy posortować wszystkie pozostałe sufiksy w czasie liniowym! Czy to nie jest niesamowite?

Wyzwanie

Podany ciąg znaków zakłada, że ​​jest zakończony znakiem specjalnym, który jest mniejszy niż jakikolwiek inny znak w tym ciągu (np. Mniejszy niż nawet bajt zerowy). Wypisuje znak typu corrosponding dla każdego przyrostka.

Można dowolnie wybrać, które char do użytku, do którego typu, ale wolałbym L, S and *na L-, S- and LMS-typetak długo, jak są one wszystkie do druku ( 0x20 - 0x7E).

Przykład

Biorąc pod uwagę ciąg mmiissiissiippiwyjściowy (przy użyciu L, S and *):

 LL*SLL*SLL*SLLL

Na przykład pierwszy Lwynika z faktu, że mmiissiissiippi$jest leksykograficznie większy niż miissiissiippi$( $reprezentuje dodany minimalny znak):

L - mmiissiissiippi$ > miissiissiippi$
L - miissiissiippi$  > iissiissiippi$
* - iissiissiippi$   < issiissiippi     and preceeded by L
S - issiissiippi$    < ssiissiippi$
L - ssiissiippi$     > siissiippi$
L - siissiippi$      > iissiippi$
* - iissiippi$       < issiippi$        and preceeded by L
S - issiippi$        < ssiippi$
L - ssiippi$         > siippi$
L - siippi$          > iippi$
* - iippi$           < ippi$            and preceeded by L
S - ippi$            < ppi$
L - ppi$             > pi$
L - pi$              > i$
L - i$               > $

Kilka innych przykładów:

"hello world" -> "L*SSL*L*LLL"
"Hello World" -> "SSSSL*SSLLL"
"53Ab§%5qS"   -> "L*SSL*SLL"

Cel

Nie jestem tu po to, by drażnić Petera Cordesa (kiedyś zrobię to przy przepełnieniu stosu); Jestem po prostu bardzo leniwy, więc to jest oczywiście ! Najkrótsza odpowiedź w bajtach wygrywa.


Edycja: Kolejność znaków jest podana według wartości bajtów. Oznacza to porównanie powinno być jak C użytkownika strcmp.

Edycja2: Jak podano w komentarzach, wyjście powinno być pojedynczym znakiem dla każdego znaku wejściowego. Chociaż założyłem, że byłoby to rozumiane jako „zwróć ciąg”, wydaje się, że co najmniej 1 odpowiedź zwraca listę pojedynczych znaków. Aby nie unieważniać istniejących odpowiedzi, pozwolę ci zwrócić listę pojedynczych znaków (lub liczb całkowitych, które po wydrukowaniu dają tylko 1 znak).


Wskazówki dotyczące czasu liniowego:

  1. Można to zrobić w 2 równoległych iteracjach do przodu lub w jednej iteracji do tyłu.
  2. Stan każdego sufiksu zależy tylko od pierwszych 2 znaków i rodzaju drugiego.
  3. Skanując dane wejściowe w odwrotnym kierunku, możesz określić L lub S w następujący sposób: $t=$c<=>$d?:$t(PHP 7), gdzie $cjest obecny znak $dpoprzedniego i $tpoprzedniego typu.
  4. Zobacz moją odpowiedź na PHP . Jutro przyznam nagrodę.
Christoph
źródło
To jest moje pierwsze pytanie :) Piaskownica otrzymała dwa głosy poparcia i żadnych komentarzy, więc myślę, że jest gotowa do opublikowania. Zapraszam do sugestii!
Christoph
Jakie znaki mogą pojawić się na wejściu?
Martin Ender
@ MartinEnder wszystkie znaki, które obsługuje Twój ciąg znaków, np. Nawet bajt zerowy dla c++ciągów stylów. Pomyśl o tym jak o danych binarnych.
Christoph
Co *znaczy
Leaky Nun
@LeakyNun *oznacza, że ​​odpowiedni sufiks jest typu left most s-type. A S-type suffix that is preceeded by a L-type suffix..
Christoph

Odpowiedzi:

7

Haskell , 64 53 48 42 bajtów

(0!)
k!(x:y)|x:y>y=1:2!y|2>1=k:0!y
_![]=[]

Wypróbuj online!

Bez golfa, z Charzamiast Int:

suffixes :: String -> String
suffixes = go 'S'
 where
   go :: Char -> String -> String
   go _ "" = ""
   go lorstar s | s > tail s = 'L' : go '*' (tail s)
                | otherwise  = lorstar : go 'S' (tail s)
bartavelle
źródło
Funkcje anonimowe są dozwolone, więc z=można je usunąć.
Ørjan Johansen
Po prostu nie umiem czytać Haskella. Czy mógłbyś podać mi krótkie wyjaśnienie?
Christoph
1
@Christoph: gofunkcja przyjmuje dwa argumenty. Pierwszy to znak reprezentujący to, co powinno być użyte do opisania Ssytuacji. Drugi to ciąg. Przechodzi rekurencyjnie przez ten ciąg, usuwając pierwszy znak na każdym kroku (właśnie to tailrobi). Sztuczka polega na tym, że pierwszy argument jest ustawiony, *gdy poprzedni wynik był a L, lub w Sinny sposób. W ten sposób, w przypadku, *gdy Snależy użyć an lub an , pierwszy argument można zastosować bezpośrednio. Mam nadzieję, że to ma sens.
bartavelle,
To całkiem niezły pomysł! Mam nadzieję zobaczyć bardziej sprytne pomysły :)
Christoph
@ ØrjanJohansen jak mam przygotować wynik w TIO?
bartavelle,
6

Galaretka ,  25 23 21 20  19 bajtów

Ṛ;\UỤỤIṠµI2n×ịØDṚ;0

Pełny program, który drukuje listę znaków, używając:

L: 0
S: 8
*: 9

(Jako link zwraca listę, w której wszystkie elementy są znakami, z wyjątkiem ostatniego, który wynosi zero.)

Wypróbuj online! lub zobacz pakiet testowy (z konwersją naLS*).

W jaki sposób?

Ṛ;\UỤỤIṠµI2n×ịØDṚ;0 - Link: list of characters, s  e.g. "cast"
Ṛ                   - reverse                           "tsac"
  \                 - cumulative reduce by:
 ;                  -   concatenation                   ["t","ts","tsa","tsac"]
   U                - upend (reverse each)              ["t","st","ast","cast"] (suffixes)
    Ụ               - sort indexes by value             [3,4,2,1] (lexicographical order)
     Ụ              - sort indexes by value             [4,3,1,2] (order of that)
      I             - incremental differences           [-1,-2,1] (change)
       Ṡ            - sign                              [-1,-1,1] (comparisons)
        µ           - monadic chain separation, call that x
         I          - incremental differences           [0,2] (only (-1,1) produce 2s)
          2         - literal 2                         2
           n        - not equal?                        [1,0] (indexes of * will be 0)
            ×       - multiply by x (vectorises)        [-1,0,1] (make indexes of *s 0)
              ØD    - decimal yield                     "0123456789"
             ị      - index into (1-indexed & modular)  ['8','9','0']
                Ṛ   - reverse                           ['0','9','8']
                 ;0 - concatenate a zero                ['0','9','8',0]
                    - implicit print                     0980
                    -                              i.e. "L*SL"
Jonathan Allan
źródło
Czy mógłbyś dodać dla mnie małe wyjaśnienie?
Christoph
2
Zrobię oczywiście - najpierw myślę o możliwych golfach ...
Jonathan Allan,
17 bajtów
Leaky Nun
@LeakyNun Jak ci się to udało ?! Używasz błąd nie myślę +o strun wydaje vectorise ale efekty bazowe nie są faktycznie Jelly iterables ale struny (np spróbować (!) +@/L€Lub +@/L€€lub ...)
Jonathan Allan
@JonathanAllan tak, +produkuje ciąg znaków. Jest to nieudokumentowana funkcja lub coś, co nazywacie błędem.
Leaky Nun
3

Python 3, 92 87 74 69 65 bajtów

s=input()
c=1
while s:d=s<s[1:];print(d+(c<d),end='');s=s[1:];c=d

Używa 0do L, 1do Si 2do *. Zawiń łańcuch wejściowy w znaki cudzysłowu; Wierzę, że jest to dozwolone przez konwencję.

Wypróbuj online!

Przykładowe zastosowanie:

mmiissiissiippi
002100210021000

zapisano 5 bajtów dzięki Leaky Nun, 4 bajty dzięki ovs

L3viathan
źródło
3

JavaScript (ES6), 51 45 bajtów

f=(c,d)=>c&&(d<(d=c<(c=c.slice(1))))+d+f(c,d)

Zaoszczędź 6 bajtów dzięki @Neil.

Rekurencyjne rozwiązanie ćwiczenia.

f=(c,d)=>c&&(d<(d=c<(c=c.slice(1))))+d+f(c,d)

console.log(f('mmiissiissiippi')); //LL*SLL*SLL*SLLL   002100210021000
console.log(f('hello world'));     //L*SSL*L*LLL       02110202000
console.log(f('Hello World'));     //SSSSL*SSLLL       11110211000
console.log(f('53Ab§%5qS'));       //L*SSL*SLL         021102100

Rick Hitchcock
źródło
Zaoszczędź 6 bajtów:f=(c,d)=>c&&(d<(d=c<(c=c.slice(1))))+d+f(c,d)
Neil,
Dzięki, @Neil, wiedziałem, że musi być gdzieś tam jakaś optymalizacja.
Rick Hitchcock,
2

JavaScript (ES6), 52 bajty

f=
s=>s.replace(/./g,_=>(c<(c=s<(s=s.slice(1))))+c,c=1)
<input oninput=o.textContent=f(this.value)><pre id=o>

Port odpowiedzi @ L3viathan.

Neil
źródło
1
@ RickHitchcock Ups, jakoś udało mi się portować c=1jako c=0...
Neil,
1

Haskell , 77 75 bajtów, czas liniowy

f(a:b:c)|let g"L"|a<b="SL";g"S"|a>b="L*";g d=d++d;d:e=f$b:c=g[d]++e
f _="L"

Wypróbuj online!

Jak to działa

Wykorzystuje rekurencję, usuwając jeden znak na raz od początku łańcucha. (Typ łańcucha Haskell jest pojedynczo połączoną listą znaków, więc każdy z tych kroków ma charakter stały).

  • Dla łańcuchów abc gdzie i b są pojedyncze znaki, a C jest dowolny (ewentualnie puste) ciąg
    • f ( abc ) = SL e , jeśli f ( bc ) = L e i a < b ;
    • f ( abc ) = L * e , jeśli f ( bc ) = S e i a > b ;
    • f ( abc ) = LL e , jeśli f ( bc ) = L e i ab ;
    • f ( abc ) = SS e , jeżeli f ( bc ) = S e i ab .
  • W przypadku ciągu jednego znaku a , f ( a ) = L.
Anders Kaseorg
źródło
1
Czy możesz podać wyjaśnienie?
R. Kap
Podaj opis, abym mógł potwierdzić, że działa to w czasie liniowym.
Christoph
@Christoph Dodano.
Anders Kaseorg
@AndersKaseorg dzięki za dodanie! Niestety wydaje się to dość gadatliwe w porównaniu do innej odpowiedzi Haskella. Czy można dalej grać w golfa, nie używając S, L and *?
Christoph
1
@Christoph Aby być jasnym, [1,1,2,0,1,1,2,0,1,1,2,0,1,1,1]to lista liczb jednocyfrowych, a nie lista pojedynczych znaków. W moim przypadku wydaje mi się, że podanie listy liczb nie uratuje mi żadnych bajtów.
Anders Kaseorg,
1

Python 2 , 65 55 bajtów

Wersja rekurencyjna, oparta na odpowiedzi L3viathan , wykorzystująca 012jako LS*:

def g(s,d=2):c=s<s[1:];return s and`c+(d<c)`+g(s[1:],c)

Wypróbuj online!

Python 3 , 65 59 bajtów

Roztwór rekurencyjne użyciu L, Si *:

f=lambda s:s and('LS'[s<s[1:]]+f(s[1:])).replace('LS','L*')

Prowadzi ciąg od przodu i zastępuje wszystkie wystąpienia LS zL*

Wypróbuj online!

TFeld
źródło
1
blah if s else''s and blahzapisuje sześć bajtów. W Pythonie 2 str(blah)`blah`zapisuje kolejne trzy bajty na drugim rozwiązaniu.
Anders Kaseorg
1

PHP, 82 bajty, czas liniowy

for($a=$argn;a&$c=$a[$i-=1];$d=$c)$a[$i]=2+$t=$d<=>$c?:$t;echo strtr($a,[13=>12]);

Przechodzi nad wejściem od prawej do lewej i zamienia każdy znak na typ.

$t=$d<=>$c?:$t

Oblicza typ na podstawie bieżącego i poprzedniego znaku (-1 lub 1). Jeśli równy, typ się nie zmienia.

Christoph
źródło
+1 za pomysł zstrtr
Jörg Hülsermann
1

PHP , 70 bajtów

L = 1, S = 0, * = 2

Obsługa wielobajtowa jest potrzebna w ostatnim Testcase z §+3 Bytes mb_substrzamiastsubstr

for(;$s=&$argn;$s=$u)$r.=$l=($l&1)+(1&$l^($s>$u=substr($s,1)));echo$r;

Wypróbuj online!

PHP , 71 bajtów

L = 1, S = 0, * = 2

for(;$s=&$argn;$s=$u)$r.=+($s>$u=substr($s,1));echo strtr($r,[10=>12]);

Wypróbuj online!

PHP , 74 bajty

for(;$s=&$argn;$s=$u)$r.=SL[$s>$u=substr($s,1)];echo strtr($r,[LS=>"L*"]);

Wypróbuj online!

Jörg Hülsermann
źródło
$s=&$argncałkiem sprytne! Jestem prawie pewien, że jest lepsza odpowiedź;) Mam nadzieję, że ktoś ją wymyśli :)
Christoph
@Christoph Mam wrażenie, że coś mi umknęło. Staram się przechowywać ostatnie LS * w varibale, ale jest ono dłuższe
Jörg Hülsermann
@Christoph znaczy, że lubisz? Naprawdę nie widziałem, dlaczego ostatnia walizka jest fałszywa. Wypróbuj online!
Jörg Hülsermann
@Christoph Dobra. Widziałem, dlaczego to nie działa w przypadku ostatniego zestawu testów, którego muszę użyć mb_substrzamiast, substrjeśli dane wejściowe nie są w prostym zakresie ascii. Czy konieczne jest wsparcie ostatniej skrzynki testowej?
Jörg Hülsermann
1
@Christoph Dziękuję W tym przypadku ignoruję ostatnią próbę z§
Jörg Hülsermann