Periodi locali di stringhe


20

Periodi locali

Prendi una stringa non vuota s . Il periodo locale di s all'indice i è il più piccolo intero positivo n tale che per ogni 0 ≤ k <n , abbiamo s [i + k] = s [i-n + k] ogni volta che vengono definiti entrambi i lati. In alternativa, è la lunghezza minima di una stringa non vuota w tale che se la concatenazione ww viene posizionata accanto a s in modo che la seconda copia di w inizi all'indice i di s , le due stringhe concordano ovunque si sovrappongano.

Ad esempio, calcoliamo il periodo locale di s = "abaabbab" nell'indice 2 (basato su 0).

  • Prova n = 1 : quindi s [2 + 0] ≠ s [2-1 + 0] , quindi questa scelta non è corretta.
  • Prova n = 2 : quindi s [2 + 0] = s [2-2 + 0] ma s [2 + 1] ≠ s [2-2 + 1] , quindi anche questo non è corretto.
  • Prova n = 3 : quindi s [2 + 0-3] non è definito, s [2 + 1] = s [2-3 + 1] e s [2 + 2] = s [2-3 + 2] . Quindi il periodo locale è 3.

Ecco una visualizzazione dei periodi locali usando la seconda definizione, con punti e virgola aggiunti tra le due copie di w per chiarezza:

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

Nota che w non è necessariamente una sottostringa di s . Questo succede qui nel caso index-4.

L'obiettivo

Il vostro contributo è una stringa non vuota s di caratteri ASCII minuscoli. Se lo si desidera, può essere preso come un elenco di caratteri. L'output deve essere l'elenco contenente il periodo locale di s in ciascuno dei suoi indici. Nell'esempio sopra, l'output corretto sarebbe [1,2,3,1,6,1,3,2] .

Vince il conteggio dei byte più basso in ogni lingua. Si applicano le regole standard del .

Casi test

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]

@Arnauld Puoi sempre trovare una w con la stessa lunghezza di s . Nel caso di qwertyuiop, w sarà una versione ruotata di qwertyuiop. Vedi anche l'esempio all'indice 4: w non è necessariamente una sottostringa di s .
Zgarb,

Ha senso. Ho letto male la sfida.
Arnauld,

Bonus immaginario per una soluzione temporale lineare! (qualcun altro può offrire una vera ricompensa, quindi continua a provare)
user202729,

Sfida davvero ordinata, ma mi chiedo se avrebbe più senso definire il periodo locale di ogni posizione tra due personaggi (cioè ovunque ;sia nel tuo esempio). Ciò eliminerebbe il primo 1.
Martin Ender il

@MartinEnder Sarebbe concettualmente più pulito, ma questa definizione semplifica la produzione dell'output eseguendo il ciclo sulla stringa e l'output non sarà vuoto.
Zgarb,

Risposte:


4

Retina , 89 86 byte

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

Provalo online! Modifica: 3 byte salvati grazie a @MartinEnder. Spiegazione:

.
$`¶$<'¶

Dividi l'input per ogni carattere, creando una coppia di righe, una per il prefisso e una per il suffisso del prefisso.

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

Esegui il resto dello script su ciascuna coppia risultante.

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

Trova tutte le partite sovrapposte ed elenca i risultati. (Vedi sotto.)

G`.

Scarta la partita vuota.

%C`.

Prendi la lunghezza di ogni partita.

N`

Ordina numericamente.

0G`

Prendi il più piccolo.

La corrispondenza funziona dividendo il prefisso e il suffisso in tre parti. Esistono quattro casi validi da considerare:

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

La regex quindi consente solo A e C di abbinare su un lato alla volta.


$&$'è uguale a $<'e la lunghezza delle linee di calcolo è inferiore con %C`.. tio.run/##K0otycxLNPz/X49LJeHQNhUb9UPbuPQ14mr0tDUPbdPT1o/…
Martin Ender

4

Java 8, 167 154 152 byte

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 byte grazie a @ceilingcat .

Provalo online.

Spiegazione:

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

1

JavaScript (ES6), 84 byte

Accetta input come una matrice di caratteri.

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)

Casi test


Non sono sicuro che sia consentito prendere una serie di caratteri, sei sicuro che non siano solo stringhe di 1 carattere?
Erik the Outgolfer,

@EriktheOutgolfer Non esiste alcun tipo di carattere in JS, quindi sì: è tecnicamente un array di stringhe di 1 carattere. La mia comprensione è che se si rompe come una stringa, è una stringa. (Ecco un meta post a riguardo, ma potrebbe esistere uno più pertinente - o che in realtà contraddice la mia ipotesi.)
Arnauld

1
O per dirla in altre parole: questo è il più vicino possibile a un elenco di personaggi in JS, che è stato esplicitamente consentito dall'OP.
Arnauld,

1

Rubino , 104 102 byte

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

Provalo online!

Un lambda che accetta una stringa e restituisce un array.

-2 byte: scambia gli endpoint di intervallo con protezioni associate all'indice

Ungolfed:

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

1

Japt , 33 32 byte

Salvato 1 byte grazie a @Shaggy

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

Provalo online!

Spiegazione

¬Ë@¯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.

Il mio primo pensiero è stato quello di confrontare ogni personaggio nella sottostringa sinistra con il carattere corrispondente nella sottostringa destra, come nella risposta JS. Ciò non funzionerebbe, tuttavia, poiché il metodo di Japt per ottenere un personaggio si sposta sull'altra estremità della stringa se l'indice è negativo o troppo grande.

Invece, la mia soluzione crea una regex dalla seconda sottostringa e la verifica sulla prima sottostringa. Prendiamo il quinto oggetto nel test-case abaabbabcome esempio:

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

Il trucco principale è che ^può abbinarsi all'infinito, fino a quando un personaggio reale è abbinato. Questo ci consente di ignorare qualsiasi numero di caratteri dall'inizio della regex, garantendo al contempo che tutti gli altri vengano abbinati consecutivamente, finendo alla fine della stringa di test.

Non sono sicuro di averlo spiegato molto bene, quindi per favore fatemi sapere se c'è qualcosa che vorreste chiarire o qualcos'altro che dovrebbe essere spiegato.



@Shaggy Grazie, quel punto e virgola mi infastidiva: P
ETHproductions

1

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

  • Salvato un byte. Golfato !b&&printfa b||printf.
  • Salvato due byte grazie a Kevin Cruijssen . Rimosse le forparentesi del corpo del loop manipolando il printfposizionamento.
  • Salvato un byte. Golfato b+=S[i+k]!=S[i-n+k]a b|=S[i+k]-S[i-n+k].
  • Ho salvato undici byte. Rimossa la necessità l=strlen(S)condizionando entrambi i loop di gestione delle stringhe da interrompere quando si raggiunge la fine della stringa (un byte null '\0').
  • Salvato due byte. Golfato i-n+k>~0a i-n>~k.
  • Salvati tre byte grazie a ceilingcat ; b||printf("|"),n++è 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];}

Provalo online!



@KevinCruijssen Grazie.
Jonathan Frech,

@ceilingcat Grazie; equivalenza ordinata, quella.
Jonathan Frech,

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.