Di che tipo sono i miei suffissi?


10

Intro

Quindi sto sprecando di nuovo il mio tempo alla ricerca di algoritmi di ordinamento dei suffissi, valutando nuove idee a mano e nel codice. Ma faccio sempre fatica a ricordare il tipo dei miei suffissi! Puoi dirmi di che tipo sono i miei suffissi?

Più a sinistra cosa?

Molti algoritmi di ordinamento dei suffissi (SAIS, KA, il mio daware) raggruppano i suffissi in diversi tipi per ordinarli. Ci sono due tipi fondamentali: di tipo S e L-type suffissi. I suffissi di tipo S sono suffissi lessicograficamente inferiori ( S maller) rispetto al suffisso seguente e di tipo L se è lessicograficamente maggiore ( arger L ). Un tipo S più a sinistra ( tipo LMS ) è proprio questo: un suffisso di tipo S preceduto da un suffisso di tipo L.

La particolarità di questi suffissi di tipo LMS è che una volta ordinati li possiamo ordinare tutti gli altri suffissi in tempo lineare! Non è fantastico?

La sfida

Data una stringa, supponiamo che sia terminata da un carattere speciale che è inferiore a qualsiasi altro carattere in quella stringa (ad es. Più piccolo del byte nullo). Emette un carattere corrosponding di tipo per ciascun suffisso.

Si può liberamente scegliere quale carattere da utilizzare per quale tipo, ma io preferirei L, S and *per L-, S- and LMS-typetutto il tempo che sono tutti stampabili ( 0x20 - 0x7E).

Esempio

Dato l' mmiissiissiippioutput della stringa (quando si utilizza L, S and *):

 LL*SLL*SLL*SLLL

Ad esempio il primo Lè dovuto al fatto che mmiissiissiippi$è lessicograficamente maggiore di miissiissiippi$(il $rappresenta il carattere minimo aggiunto):

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$               > $

Alcuni altri esempi:

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

Obbiettivo

Non sono qui per infastidire Peter Cordes (un giorno lo farò su StackOverflow); Sono solo molto pigro, quindi questo è ovviamente ! Vince la risposta più breve in byte.


Modifica: l'ordine dei caratteri è dato dal loro valore in byte. Ciò significa confrontare dovrebbe essere come C di strcmp.

Modifica2: come indicato nei commenti, l'output deve essere un singolo carattere per ciascun carattere di input. Mentre supponevo che sarebbe stato inteso come "restituisci una stringa" sembra che almeno 1 risposta restituisca un elenco di singoli caratteri. Per non invalidare le risposte esistenti, ti permetterò di restituire un elenco di singoli caratteri (o numeri interi che, se stampati, producono solo 1 carattere).


Suggerimenti per il tempo lineare:

  1. Può essere eseguito in 2 iterazioni parallele in avanti o in una singola iterazione all'indietro.
  2. Lo stato di ciascun suffisso dipende solo dai primi 2 caratteri e dal tipo del secondo.
  3. Scansionando l'ingresso in direzione inversa è possibile determinare L o S in questo modo: $t=$c<=>$d?:$t(PHP 7), dove $cè il carattere corrente $ddel tipo precedente e $tprecedente.
  4. Vedi la mia risposta PHP . Domani assegnerò la taglia.

Questa è la mia prima domanda :) Sandbox ha ottenuto due voti e nessun commento, quindi penso che sia pronto per essere pubblicato. Sentiti libero di dare suggerimenti!
Christoph,

Quali caratteri possono apparire nell'input?
Martin Ender,

@MartinInserisci tutti i caratteri supportati dalla stringa, ad esempio anche il byte null per le c++stringhe di stile. Pensalo come dati binari.
Christoph,

Cosa *significa?
Leaky Nun,

@LeakyNun *significa che il suffisso corrispondente è di tipo left most s-type. A S-type suffix that is preceeded by a L-type suffix..
Christoph,

Risposte:


7

Haskell , 64 53 48 42 byte

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

Provalo online!

Ungolfed, con Charinvece di 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)

Sono consentite funzioni anonime, quindi z=possono essere rimosse.
Ørjan Johansen,

Non riesco proprio a leggere Haskell. Ti dispiacerebbe darmi una breve spiegazione?
Christoph,

1
@Christoph: la gofunzione accetta due argomenti. Il primo è il personaggio che rappresenta ciò che dovrebbe essere usato per descrivere la Ssituazione. Il secondo è una stringa. Attraversa ricorsivamente quella stringa, rimuovendo il primo carattere ad ogni passo (ecco cosa tailfa). Il trucco è che il primo argomento è impostato su *quando il risultato precedente era un L, o Saltrimenti. In questo modo, nel caso in cui si debba usare un *o un S, quel primo argomento può essere usato direttamente. Spero che abbia un senso.
bartavelle,

È una bella idea! Spero di vedere idee più intelligenti :)
Christoph,

@ ØrjanJohansen come dovrei preparare il risultato in TIO?
bartavelle,

6

Gelatina ,  25 23 21 20  19 byte

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

Un programma completo che stampa l'elenco dei caratteri, usando:

L: 0
S: 8
*: 9

(Come collegamento restituisce un elenco in cui tutti gli elementi sono caratteri tranne l'ultimo, che è uno zero.)

Provalo online! oppure vedi la suite di test (con conversione inLS*).

Come?

Ṛ;\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"

Ti dispiacerebbe aggiungere una piccola spiegazione per me?
Christoph,

2
Lo farò ovviamente - penso prima ai possibili golf ...
Jonathan Allan il


@LeakyNun Come hai fatto a risolverlo ?! Stai usando un bug lì, penso che le +stringhe sembrino vettorializzare, ma i risultati sottostanti non sono in realtà Jelly iterabili ma stringhe (!) (Ad esempio, prova +@/L€o +@/L€€o ...)
Jonathan Allan,

@JonathanAllan sì, +produce una stringa effettiva. Questa è una funzionalità non documentata, o ciò che chiamate bug.
Leaky Nun,


3

JavaScript (ES6), 51 45 byte

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

6 byte salvati grazie a @Neil.

Una soluzione ricorsiva all'esercizio.

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


Salva 6 byte:f=(c,d)=>c&&(d<(d=c<(c=c.slice(1))))+d+f(c,d)
Neil,

Grazie, @Neil, sapevo che ci doveva essere un'ottimizzazione lì da qualche parte.
Rick Hitchcock,

2

JavaScript (ES6), 52 byte

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>

Porta della risposta di @ L3viathan.


1
@RickHitchcock Oops, in qualche modo sono riuscito a port c=1come c=0...
Neil,


1

Haskell , 77 75 byte, tempo lineare

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"

Provalo online!

Come funziona

Questo utilizza la ricorsione, togliendo un carattere alla volta dall'inizio della stringa. (Il tipo di stringa Haskell è un elenco di caratteri collegati singolarmente, quindi ciascuno di questi passaggi è a tempo costante.)

  • Per una stringa abc dove un e b sono singoli caratteri e c è qualsiasi stringa (eventualmente vuoto),
    • f ( abc ) = SL e , se f ( bc ) = L e ed un < b ;
    • f ( abc ) = L * e , se f ( bc ) = S e e a > b ;
    • f ( abc ) = LL e , se f ( bc ) = L e e ab ;
    • f ( abc ) = SS e , se f ( bc ) = S e e ab .
  • Per una stringa a carattere singolo a , f ( a ) = L.

1
La prego di fornire una spiegazione?
R. Kap,

Fornisci una descrizione in modo che io possa confermare che questo viene eseguito in tempo lineare.
Christoph,

@Christoph Aggiunto.
Anders Kaseorg,

@AndersKaseorg grazie per l'aggiunta! Purtroppo questo sembra abbastanza prolisso rispetto all'altra risposta di Haskell. Questo potrebbe essere golfato ulteriormente non usando S, L and *?
Christoph,

1
@Christoph Per essere chiari, [1,1,2,0,1,1,2,0,1,1,2,0,1,1,1]è un elenco di numeri a una cifra, non un elenco di singoli caratteri. Nel mio caso, penso che l'output di un elenco di numeri non mi salverebbe alcun byte.
Anders Kaseorg,

1

Python 2 , 65 55 byte

Versione ricorsiva, basata sulla risposta di L3viathan , usando 012come LS*:

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

Provalo online!

Python 3 , 65 59 byte

Soluzione ricorsiva utilizzando L, Se *:

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

Esegue la stringa dalla parte anteriore e sostituisce tutte le istanze di LSwithL*

Provalo online!


1
blah if s else''s and blahsalva sei byte. In Python 2, str(blah)`blah`salva altri tre byte sulla seconda soluzione.
Anders Kaseorg,

1

PHP, 82 byte, tempo lineare

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

Cammina sull'input da destra a sinistra e sostituisce ogni carattere con il tipo.

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

Calcola il tipo dato il carattere corrente e precedente (-1 o 1). Se uguale il tipo non cambia.


+1 per l'idea constrtr
Jörg Hülsermann,

1

PHP , 70 byte

L = 1, S = 0, * = 2

Multibyte supporto è necessario per l'ultimo Testcase con i §+3 Bytes mb_substrinvecesubstr

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

Provalo online!

PHP , 71 byte

L = 1, S = 0, * = 2

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

Provalo online!

PHP , 74 byte

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

Provalo online!


$s=&$argnabbastanza intelligente! Sono abbastanza sicuro che ci sia una risposta migliore;) Speriamo che qualcuno la trovi :)
Christoph,

@Christoph Ho la sensazione che mi manchi qualcosa. Ho provato a memorizzare l'ultimo LS * in un varibale ma è più lungo
Jörg Hülsermann

@Christoph vuoi dire che ti piace? Non ho davvero visto perché l'ultimo testcase è falso Provalo online!
Jörg Hülsermann,

@Christoph Okay l'ho visto perché non funziona per l'ultimo testcase che devo usare mb_substrinvece che substrse l'input non è nel semplice intervallo ASCII. È necessario supportare l'ultimo testcase?
Jörg Hülsermann,

1
@Christoph Grazie In questo caso ignoro l'ultimo testcase con§
Jörg Hülsermann,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.