¿De qué tipo son mis sufijos?


10

Introducción

Así que he estado perdiendo mi tiempo nuevamente investigando algoritmos de clasificación de sufijos, evaluando nuevas ideas a mano y en código. ¡Pero siempre me cuesta recordar el tipo de mis sufijos! ¿Me puede decir de qué tipo son mis sufijos?

¿Qué más a la izquierda?

Una gran cantidad de algoritmos de clasificación de sufijos (SAIS, KA, mi propio software daware) agrupan los sufijos en diferentes tipos para ordenarlos. Hay dos tipos básicos: de tipo S y tipo L sufijos. Los sufijos tipo S son sufijos que son lexicográficamente menos ( S menor) que el siguiente sufijo y tipo L si es lexicográficamente mayor ( L arger). Un tipo S más a la izquierda ( tipo LMS ) es solo eso: un sufijo de tipo S precedido por un sufijo de tipo L.

¡Lo especial de estos sufijos de tipo LMS es que una vez que los ordenamos, podemos ordenar todos los demás sufijos en tiempo lineal! ¿No es asombroso?

El reto

Dada una cadena, suponga que está terminada por un carácter especial que es menor que cualquier otro carácter en esa cadena (por ejemplo, más pequeño que incluso el byte nulo). Salida de un tipo de corrosponding char para cada sufijo.

Se puede elegir libremente qué char a utilizar para el tipo, pero yo preferiría L, S and *para L-, S- and LMS-typesiempre y cuando todos ellos son imprimibles ( 0x20 - 0x7E).

Ejemplo

Dado el mmiissiissiippiresultado de la cadena (cuando se usa L, S and *):

 LL*SLL*SLL*SLLL

Por ejemplo, el primero Lse debe al hecho de que mmiissiissiippi$es lexicográficamente mayor que miissiissiippi$( $representa el carácter mínimo agregado):

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

Algunos ejemplos más:

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

Objetivo

No estoy aquí para molestar a Peter Cordes (voy a hacer esto en stackoverflow en algún momento); Soy muy vago, así que, por supuesto, ¡esto es ! La respuesta más corta en bytes gana.


Editar: el orden de los caracteres viene dado por su valor de byte. Eso significa que se comparan deben ser como C de strcmp.

Edit2: como se indica en la salida de comentarios, debe haber un solo carácter para cada carácter de entrada. Si bien supuse que se entendería como "devolver una cadena", parece que al menos 1 respuesta devuelve una lista de caracteres individuales. Para no invalidar las respuestas existentes, le permitiré devolver una lista de caracteres individuales (o enteros que, cuando se imprimen, dan como resultado solo 1 carácter).


Consejos para tiempo lineal:

  1. Se puede hacer en 2 iteraciones paralelas hacia adelante o en una única iteración hacia atrás.
  2. El estado de cada sufijo depende solo de los primeros 2 caracteres y del tipo del segundo.
  3. Al escanear la entrada en dirección inversa, puede determinar L o S de esta manera: $t=$c<=>$d?:$t(PHP 7), donde $cestá el carácter actual $ddel tipo anterior y $tanterior.
  4. Ver mi respuesta PHP . Mañana otorgaré la recompensa.

Esta es mi primera pregunta :) Sandbox recibió dos votos a favor y ningún comentario, así que creo que está listo para ser publicado. Siéntete libre de hacer sugerencias !
Christoph

¿Qué caracteres pueden aparecer en la entrada?
Martin Ender

@MartinEnder todos los caracteres que admite su cadena, por ejemplo, incluso el byte nulo para c++cadenas de estilo. Piense en ello como datos binarios.
Christoph

Que *significa
Leaky Nun

@LeakyNun *significa que el sufijo correspondiente es de tipo left most s-type. A S-type suffix that is preceeded by a L-type suffix..
Christoph

Respuestas:


7

Haskell , 64 53 48 42 bytes

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

Pruébalo en línea!

Sin golf, con en Charlugar de 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)

Se permiten funciones anónimas, por lo que z=se pueden eliminar.
Ørjan Johansen

Simplemente no puedo leer a Haskell. ¿Te importaría darme una breve explicación?
Christoph

1
@ Christoph: la gofunción toma dos argumentos. El primero es el personaje que representa lo que debe usarse para describir la Ssituación. El segundo es una cadena. Pasa por esa cadena de forma recursiva, eliminando el primer carácter en cada paso (eso es lo que tailhace). El truco es que el primer argumento se establece en *cuando el resultado anterior fue a L, o de lo Scontrario. De esa manera, en el caso en que se debe usar an *o an S, ese primer argumento se puede usar directamente. Espero que tenga sentido.
bartavelle

¡Esa es una buena idea! Espero ver ideas más inteligentes :)
Christoph

@ ØrjanJohansen, ¿cómo se supone que debo preparar el resultado en TIO?
bartavelle

6

Jalea ,  25 23 21 20  19 bytes

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

Un programa completo que imprime la lista de caracteres, usando:

L: 0
S: 8
*: 9

(Como enlace, devuelve una lista donde todos los elementos son caracteres excepto el último, que es un cero).

Pruébalo en línea! o vea el conjunto de pruebas (con conversión aLS*).

¿Cómo?

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

¿Te importaría agregar una pequeña explicación para mí?
Christoph

2
Lo haré, por supuesto, primero estoy pensando en posibles campos de golf ...
Jonathan Allan


@LeakyNun ¿Cómo resolviste eso? Está utilizando un error no creo +en las cadenas parece vectorize pero los resultados subyacentes no esté jalea iterables pero las cadenas (por ejemplo, tratar (!) +@/L€O +@/L€€, o ...)
Jonathan Allan

@JonathanAllan sí, +produce una cadena real. Esta es una característica no documentada, o lo que llamas error.
Leaky Nun


3

JavaScript (ES6), 51 45 bytes

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

Guardado 6 bytes gracias a @Neil.

Una solución recursiva para el ejercicio.

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


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

Gracias, @Neil, sabía que tenía que haber una optimización en alguna parte.
Rick Hitchcock

2

JavaScript (ES6), 52 bytes

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>

Puerto de la respuesta de @ L3viathan.


1
@RickHitchcock Vaya, de alguna manera logré portar c=1como c=0...
Neil


1

Haskell , 77 75 bytes, tiempo lineal

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"

Pruébalo en línea!

Cómo funciona

Esto utiliza la recursión, quitando un carácter a la vez desde el comienzo de la cadena. (El tipo de cadena de Haskell es una lista de caracteres enlazados individualmente, por lo que cada uno de estos pasos es de tiempo constante).

  • Para una cadena abc donde a y b son caracteres individuales yc es cualquier cadena (posiblemente vacía),
    • f ( abc ) = SL e , si f ( bc ) = L e y a < b ;
    • f ( abc ) = L * e , si f ( bc ) = S e y a > b ;
    • f ( abc ) = LL e , si f ( bc ) = L e y ab ;
    • f ( abc ) = SS e , si f ( bc ) = S e y ab .
  • Para una cadena de un solo carácter a , f ( a ) = L.

1
¿Podría por favor dar una explicación?
R. Kap

Proporcione una descripción para que pueda validar que esto se ejecuta en tiempo lineal.
Christoph

@ Christoph agregado.
Anders Kaseorg

@AndersKaseorg gracias por agregar! Lamentablemente, esto parece bastante detallado en comparación con la otra respuesta de Haskell. ¿Podría esto jugar más golf al no usar S, L and *?
Christoph

1
@ Christoph Para ser claros, [1,1,2,0,1,1,2,0,1,1,2,0,1,1,1]es una lista de números de un solo dígito, no una lista de caracteres únicos. En mi caso, creo que generar una lista de números no me ahorraría ningún byte.
Anders Kaseorg

1

Python 2 , 65 55 bytes

Versión recursiva, basada en la respuesta de L3viathan , usando 012como LS*:

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

Pruébalo en línea!

Python 3 , 65 59 bytes

Solución recursiva usando L, Sy *:

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

Se ejecuta a través de la cadena desde el frente y reemplaza todas las instancias de LSconL*

Pruébalo en línea!


1
blah if s else''s and blahguarda seis bytes. En Python 2, str(blah)`blah`guarda otros tres bytes en la segunda solución.
Anders Kaseorg

1

PHP, 82 bytes, tiempo lineal

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

Camina sobre la entrada de derecha a izquierda y reemplaza cada carácter con el tipo.

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

Calcula el tipo dado el carácter actual y el carácter anterior (-1 o 1). Si es igual, el tipo no cambia.


+1 para la idea constrtr
Jörg Hülsermann

1

PHP , 70 bytes

L = 1, S = 0, * = 2

Se necesita soporte multibyte para el último caso de prueba con los §bytes +3 en su mb_substrlugarsubstr

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

Pruébalo en línea!

PHP , 71 bytes

L = 1, S = 0, * = 2

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

Pruébalo en línea!

PHP , 74 bytes

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

Pruébalo en línea!


$s=&$argnmuy inteligente ! Sin embargo, estoy bastante seguro de que hay una mejor respuesta;) Espero que alguien se le ocurra :)
Christoph

@ Christoph Tengo la sensación de que extraño algo. He intentado almacenar el último LS * en una variable pero es más largo
Jörg Hülsermann

@ Christoph quiere decir que te gusta así? Realmente no he visto por qué el último caso de prueba es falso ¡ Pruébelo en línea!
Jörg Hülsermann

@Christoph Ok, lo he visto por qué no funciona para el último caso de prueba que debo usar en mb_substrlugar de substrsi la entrada no está en el rango simple de ascii. ¿Es necesario soportar el último caso de prueba?
Jörg Hülsermann

1
@ Christoph Gracias En este caso, ignoro el último caso de prueba con§
Jörg Hülsermann
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.