Periodos locales de cuerdas

20

Períodos locales

Tome una cadena no vacía s . El período local de s en el índice i es el número entero positivo más pequeño n tal que para cada 0 ≤ k <n , tenemos s [i + k] = s [i-n + k] cada vez que se definen ambos lados. Alternativamente, es la longitud mínima de una cadena no vacía w tal que si la concatenación ww se coloca al lado de s para que la segunda copia de w comience en el índice i de s , entonces las dos cadenas coinciden donde se superponen.

Como ejemplo, calculemos el período local de s = "abaabbab" en el índice 2 (basado en 0).

  • Pruebe n = 1 : luego s [2 + 0] ≠ s [2-1 + 0] , por lo que esta opción no es correcta.
  • Pruebe n = 2 : entonces s [2 + 0] = s [2-2 + 0] pero s [2 + 1] ≠ s [2-2 + 1] , por lo que esto tampoco es correcto.
  • Pruebe n = 3 : entonces s [2 + 0-3] no está definido, s [2 + 1] = s [2-3 + 1] y s [2 + 2] = s [2-3 + 2] . Por lo tanto, el período local es 3.

Aquí hay una visualización de los períodos locales usando la segunda definición, con punto y coma agregados entre las dos copias de w para mayor claridad:

index      a b a a b b a b      period
 0       a;a                     1
 1       b a;b a                 2
 2       a a b;a a b             3
 3             a;a               1
 4     b b a b a a;b b a b a a   6
 5                 b;b           1
 6               a b b;a b b     3
 7                   b a;b a     2

Tenga en cuenta que w no es necesariamente una subcadena de s . Esto sucede aquí en el caso del índice 4.

La tarea

Su entrada es una cadena no vacía s de caracteres ASCII en minúscula. Se puede tomar como una lista de caracteres si se desea. Su salida será la lista que contiene el período local de s en cada uno de sus índices. En el ejemplo anterior, la salida correcta sería [1,2,3,1,6,1,3,2] .

El conteo de bytes más bajo en cada idioma gana. Se aplican reglas estándar de .

Casos de prueba

a -> [1]
hi -> [1, 2]
www -> [1, 1, 1]
xcxccxc -> [1, 2, 2, 5, 1, 3, 2]
abcbacb -> [1, 4, 7, 7, 7, 3, 3]
nininini -> [1, 2, 2, 2, 2, 2, 2, 2]
abaabbab -> [1, 2, 3, 1, 6, 1, 3, 2]
woppwoppw -> [1, 4, 4, 1, 4, 4, 4, 1, 4]
qwertyuiop -> [1, 10, 10, 10, 10, 10, 10, 10, 10, 10]
deededeededede -> [1, 3, 1, 5, 2, 2, 5, 1, 12, 2, 2, 2, 2, 2]
abababcabababcababcabababcaba -> [1, 2, 2, 2, 2, 7, 7, 7, 7, 2, 2, 2, 19, 19, 5, 5, 2, 5, 5, 12, 12, 2, 2, 2, 7, 7, 5, 5, 2]
Zgarb
fuente
@Arnauld Siempre puedes encontrar una w con la misma longitud que s . En el caso de qwertyuiop, w será una versión rotada de qwertyuiop. Vea también el ejemplo en el índice 4: w no es necesariamente una subcadena de s .
Zgarb
Eso tiene sentido. Leí mal el desafío.
Arnauld
¡Bono imaginario para una solución de tiempo lineal! (alguien más puede ofrecer una verdadera recompensa, así que sigue intentándolo)
user202729
Un desafío realmente bueno, pero me pregunto si tendría más sentido definir el período local de cada posición entre dos caracteres (es decir, donde sea que ;esté en su ejemplo). Eso eliminaría al líder 1.
Martin Ender
@MartinEnder Eso sería conceptualmente más limpio, pero esta definición hace que sea más fácil producir la salida haciendo un bucle sobre la cadena, y la salida no estará vacía.
Zgarb

Respuestas:

4

Retina , 89 86 bytes

.
$`¶$<'¶
/(^|.+)¶.+/_(Lw$`^(.+)?(.*)(.+)?¶(?(1)|(.*))\2(?(3)$)
$2$3$4
G`.
%C`.
N`
0G`

Pruébalo en línea! Editar: Guardado 3 bytes gracias a @MartinEnder. Explicación:

.
$`¶$<'¶

Divida la entrada en cada carácter, creando un par de líneas, una para el prefijo y otra para el sufijo del prefijo.

/(^|.+)¶.+/_(

Ejecute el resto del script en cada par resultante.

Lw$`^(.+)?(.*)(.+)?¶(?(1)|(.*))\2(?(3)$)
$2$3$4

Encuentra todas las coincidencias superpuestas y enumera los resultados. (Vea abajo.)

G`.

Descarta la cerilla vacía.

%C`.

Toma la duración de cada partido.

N`

Ordenar numéricamente.

0G`

Toma el más pequeño.

La coincidencia funciona dividiendo el prefijo y el sufijo en tres partes. Hay cuatro casos válidos a considerar:

AB|BC   B matches B to the left and B to the right
B|ABC   AB matches [A]B to the left and AB to the right
ABC|B   BC matches BC to the left and B[C] to the right
BC|AB   ABC matches [A]BC to the left and AB[C] to the right

Por lo tanto, la expresión regular solo permite que A y C coincidan en un lado a la vez.

Neil
fuente
$&$'es igual a $<'y calcular longitudes de línea es más corto con %C`.. tio.run/##K0otycxLNPz/X49LJeHQNhUb9UPbuPQ14mr0tDUPbdPT1o/…
Martin Ender
4

Java 8, 167 154 152 bytes

s->{int l=s.length,r[]=new int[l],i=0,n,k;for(;i<l;r[i++]=n)n:for(n=0;;){for(k=++n;k-->0;)if(i+k<l&i+k>=n&&s[i+k]!=s[i-n+k])continue n;break;}return r;}

-2 bytes gracias a @ceilingcat .

Pruébalo en línea.

Explicación:

s->{                          // Method with char-array parameter and int-array return-type
  int l=s.length,             //  Length of the input-array
      r[]=new int[l],         //  Result-array of the same size 
      i=0,n,k;                //  Integers `i`, `n`, and `k` as defined in the challenge
  for(;i<l;                   //  Loop `i` in the range [0, `l`):
      r[i++]=n)               //    After every iteration: Add `n` to the array
    n:for(n=0;;){             //   Inner loop `n` from 0 upwards indefinitely
      for(k=++n;k-->0;)       //    Inner loop `k` in the range [`n`, 0]:
                              //    (by first increasing `n` by 1 with `++n`)
        if(i+k<l&i+k>=n)      //     If `i+k` and `i-n+k` are both within bounds,
           &&s[i+k]!=s[i-n+k])//     and if `s[i+k]` is not equal to `s[i-n+k]`:
          continue n;         //      Continue loop `n`
                              //    If we haven't encountered the `continue n` in loop `k`:
      break;}                 //     Break loop `n`
  return r;}                  //  Return the result
Kevin Cruijssen
fuente
1

JavaScript (ES6), 84 bytes

Toma la entrada como una matriz de caracteres.

s=>s.map((_,i)=>s.some(_=>s.every(_=>k<j|!s[k]|s[k-j]==s[k++]|k-i>j,++j,k=i),j=0)*j)

Casos de prueba

Arnauld
fuente
No estoy seguro de si está permitido tomar una serie de caracteres, ¿estás seguro de que no son solo cadenas de 1 carácter?
Erik the Outgolfer
@EriktheOutgolfer No hay ningún tipo de carácter en JS, así que sí: técnicamente es una matriz de cadenas de 1 carácter. Tengo entendido que si grazna como una cuerda, es una cuerda. (Aquí hay una meta publicación sobre eso, pero puede existir una más relevante, o una que realmente contradiga mi suposición.)
Arnauld
1
O para decirlo en otras palabras: esto es lo más cerca que podemos llegar a una lista de caracteres en JS, que fue explícitamente permitido por el OP.
Arnauld
1

Ruby , 104 102 bytes

->s{l=s.size-1
(0..l).map{|i|n=0
loop{n+=1
(n-i..l-i).all?{|k|k<0||k>=n||s[i+k]==s[i-n+k]}&&break}
n}}

Pruébalo en línea!

Una lambda que acepta una cadena y devuelve una matriz.

-2 bytes: puntos finales de rango de intercambio con guardias vinculados al índice

Sin golf:

->s{
  l=s.size-1                # l is the maximum valid index into s
  (0..l).map{ |i|           # i is the current index
    n=0                     # n is the period being tested
    loop{                   # Repeat forever:
      n+=1                  # Increment n
      (n-i..l-i).all?{ |k|  # If for all k where i+k and i-n+k are valid indexes into s
        k<0 || k>=n ||      #   We need not consider k OR
          s[i+k]==s[i-n+k]  #   The characters at the relevant indexes match
      } && break            # Then stop repeating
    }
  n                         # Map this index i to the first valid n
  }
}
benj2240
fuente
1

Japt , 33 32 bytes

Guardado 1 byte gracias a @Shaggy

¬Ë@¯E f'$iUtED ú.D r."($&|^)"}aÄ

¡Pruébelo en línea!

Explicación

¬Ë@¯E f'$iUtED ú.D r."($&|^)"}aÄ   Implicit: U = input string
¬Ë                                 Split the input into chars, and map each index E to
  @                          }aÄ     the smallest positive integer D where
   ¯E                                  the first E chars of U
      f                                matches the regex formed by
          UtED                         taking D chars of U from index E,
                ú.D                     padding to length D with periods,
                    r."($&|^)"          replacing each char C with "(C|^)",
        '$i                             and placing a '$' at the very end.

Mi primer pensamiento fue comparar cada carácter en la subcadena izquierda con el carácter correspondiente en la subcadena derecha, como en la respuesta JS. Sin embargo, eso no funcionaría, ya que el método de Japt para obtener un personaje simplemente se ajusta al otro extremo de la cadena si el índice es negativo o demasiado grande.

En cambio, mi solución construye una expresión regular de la segunda subcadena y la prueba en la primera subcadena. Tomemos el quinto elemento en el caso de prueba abaabbabcomo ejemplo:

abaabbab
    ^ split point -> abaa for testing regex, bbab for making regex

   slice  regex                              matches abaa
1. b      /(b|^)$/                           no
2. bb     /(b|^)(b|^)$/                      no
3. bba    /(b|^)(b|^)(a|^)$/                 no
4. bbab   /(b|^)(b|^)(a|^)(b|^)$/            no
5. bbab.  /(b|^)(b|^)(a|^)(b|^)(.|^)$/       no
6. bbab.. /(b|^)(b|^)(a|^)(b|^)(.|^)(.|^)$/  yes: /^^ab..$/

El truco principal es que ^puede coincidir infinitamente, hasta que un personaje real coincida. Esto nos permite ignorar cualquier número de caracteres desde el comienzo de la expresión regular, al tiempo que nos aseguramos de que el resto coincida consecutivamente, terminando al final de la cadena de prueba.

No estoy seguro de haber explicado esto muy bien, así que avíseme si hay algo que le gustaría aclarar o cualquier otra cosa que deba explicarse.

ETHproducciones
fuente
32 bytes .
Shaggy
@ Shaggy Gracias, ese punto y coma me estaba molestando: P
ETHproductions
1

C (gcc) , 143 142 140 139 128 126 123 bytes

  • Guardado un byte. Golfed !b&&printfa b||printf.
  • Ahorró dos bytes gracias a Kevin Cruijssen . Se eliminaron los forparéntesis del cuerpo del bucle haciendo malabares con la printfubicación.
  • Guardado un byte. Golfed b+=S[i+k]!=S[i-n+k]a b|=S[i+k]-S[i-n+k].
  • Once bytes guardados. Se eliminó la necesidad de l=strlen(S)acondicionar ambos bucles de manejo de cadenas para que se rompan al llegar al final de la cadena (un byte nulo '\0').
  • Guardado dos bytes. Golfed i-n+k>~0a i-n>~k.
  • Guardado tres bytes gracias a ceilingcat ; b||printf("|"),n++es equivalente a n+=b||printf("|").
i,b,k,n;f(char*S){for(i=~0;S[++i];)for(b=n=1;b;n+=b||printf("%d,",n))for(b=k=0;k<n&&S[i+k];k++)b|=n-i>k?0:S[i+k]-S[i-n+k];}

Pruébalo en línea!

Jonathan Frech
fuente
Puede guardar 2 bytes quitando los corchetes y colocando b||printf("%d,",n)dentro del ciclo for: i,b,k,n,l;f(char*S){for(l=strlen(S),i=-1;++i<l;)for(b=n=1;b;b||printf("%d,",n),n++)for(b=k=0;k<n;k++)i+k<l&i-n+k>=0&&(b+=S[i+k]!=S[i-n+k]);} 140 bytes
Kevin Cruijssen
@KevinCruijssen Gracias.
Jonathan Frech
@ceilingcat Gracias; pura equivalencia, esa.
Jonathan Frech
0

Python 2 , 115 bytes

lambda s:[min(j+1for j in R(len(s))if all(s[k+~j]==s[k]for k in R(i,i-~j)if len(s)>k>j))for i in R(len(s))]
R=range

Pruébalo en línea!

Chas Brown
fuente