Períodos locais de strings


20

Períodos locais

Pegue uma string não vazia s . O período local de s no índice i é o menor número inteiro positivo n, de modo que para cada 0 ≤ k <n , temos s [i + k] = s [i-n + k] sempre que ambos os lados são definidos. Como alternativa, é o comprimento mínimo de uma sequência não vazia w, de modo que, se a concatenação ww for colocada ao lado de s, de modo que a segunda cópia de w comece no índice i de s , as duas cadeias concordam onde quer que se sobreponham.

Como exemplo, vamos calcular o período local de s = "abaabbab" no índice 2 (baseado em 0).

  • Tente n = 1 : então s [2 + 0] ≠ s [2-1 + 0] , portanto, essa opção não está correta.
  • Tente n = 2 : então s [2 + 0] = s [2-2 + 0] mas s [2 + 1] ≠ s [2-2 + 1] , portanto, isso também não está correto.
  • Tente n = 3 : em seguida, s [2 + 0-3] não é definida, s [2 + 1] = s [3/2 + 1] e do [2 + 2] = s [2-3 + 2] . Assim, o período local é 3.

Aqui está uma visualização dos períodos locais usando a segunda definição, com ponto-e-vírgula adicionado entre as duas cópias de w para maior clareza:

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

Observe que w não é necessariamente uma substring de s . Isso acontece aqui no caso do índice 4.

A tarefa

Sua entrada é uma sequência não vazia s de caracteres ASCII em minúsculas. Pode ser tomado como uma lista de caracteres, se desejado. Sua saída deve ser a lista que contém o período local de s em cada um de seus índices. No exemplo acima, a saída correta seria [1,2,3,1,6,1,3,2] .

A contagem de bytes mais baixa em cada idioma vence. Aplicam-se as regras padrão de .

Casos de teste

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 Você sempre pode encontrar w com o mesmo comprimento que s . No caso de qwertyuiop, w será uma versão rotacionada de qwertyuiop. Veja também o exemplo no índice 4: w não é necessariamente uma substring de s .
Zgarb

Isso faz sentido. Eu interpretei mal o desafio.
Arnauld

Bônus imaginário para uma solução de tempo linear! (alguém pode oferecer uma recompensa real, continue tentando) #
user202729

Desafio realmente bacana, mas me pergunto se faria mais sentido definir o período local de cada posição entre dois caracteres (ou seja, onde quer que ;esteja no seu exemplo). Isso acabaria com o principal.
Martin Ender

@MartinEnder Isso seria conceitualmente mais limpo, mas essa definição facilita a produção da saída fazendo um loop sobre a string, e a saída não ficará vazia.
Zgarb

Respostas:


4

Retina , 89 86 bytes

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

Experimente online! Editar: salvou 3 bytes graças a @MartinEnder. Explicação:

.
$`¶$<'¶

Divida a entrada em cada caractere, criando um par de linhas, uma para o prefixo e outra para o sufixo do prefixo.

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

Execute o restante do script em cada par resultante.

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

Encontre todas as correspondências sobrepostas e liste os resultados. (Ver abaixo.)

G`.

Descarte a partida vazia.

%C`.

Pegue a duração de cada partida.

N`

Classifique numericamente.

0G`

Pegue o menor.

A correspondência funciona dividindo o prefixo e o sufixo em três partes. Há quatro casos válidos a serem considerados:

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

Portanto, a regex permite apenas que A e C correspondam de um lado de cada vez.


$&$'é igual a $<'e o comprimento da linha de computação é menor com %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 graças a @ceilingcat .

Experimente online.

Explicação:

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 bytes

Recebe a entrada como uma 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 teste


Não sei se é permitido usar uma matriz de caracteres. Tem certeza de que não são apenas cadeias de caracteres de 1?
Erik the Outgolfer

@EriktheOutgolfer Não há tipo de caractere em JS, então sim: é tecnicamente uma matriz de cadeias de caracteres de 1 caractere. Meu entendimento é que, se grasna como uma corda, é uma corda. (Aqui é uma pós meta sobre isso, mas um mais relevante pode existir - ou um que realmente contradiz a minha suposição.)
Arnauld

1
Ou, em outras palavras: é o mais próximo possível de uma lista de caracteres em JS, que foi explicitamente permitida pelo 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}}

Experimente online!

Um lambda aceitando uma string e retornando uma matriz.

-2 bytes: trocar pontos de extremidade do intervalo com proteções vinculadas ao índice

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

Japonês , 33 32 bytes

Guardado 1 byte graças a @Shaggy

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

Teste online!

Explicação

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

Meu primeiro pensamento foi comparar apenas cada caractere na substring esquerda com o caractere correspondente na substring direita, como na resposta JS. Isso não funcionaria, no entanto, como o método de Japt para obter um caractere apenas passa para o outro extremo da string se o índice for negativo ou muito grande.

Em vez disso, minha solução cria um regex a partir da segunda substring e a testa na primeira substring. Vamos considerar o quinto item no caso de teste abaabbabcomo exemplo:

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

O principal truque é que ^pode corresponder infinitamente, até que um caractere real seja correspondido. Isso nos permite ignorar qualquer número de caracteres desde o início da regex, garantindo que os demais sejam correspondidos consecutivamente, terminando no final da sequência de teste.

Não sei se expliquei isso muito bem; por isso, deixe-me saber se há algo que você queira esclarecer ou qualquer outra coisa que deva ser explicada.



@ Shaygy Obrigado, esse ponto-e-vírgula estava me incomodando: P
ETHproductions

1

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

  • Guardou um byte. Jogou golfe !b&&printfpara b||printf.
  • Economizou dois bytes graças a Kevin Cruijssen . Removidos os forparênteses do corpo do loop, manipulando oprintf veiculação.
  • Guardou um byte. Jogou golfe b+=S[i+k]!=S[i-n+k]parab|=S[i+k]-S[i-n+k].
  • Onze bytes salvos. Foi removida a necessidade de l=strlen(S)condicionar o loop de manipulação de string para quebrar ao atingir o final da string (um byte nulo'\0' ).
  • Salva dois bytes. Jogou golfe i-n+k>~0parai-n>~k .
  • Economizou três bytes graças ao 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];}

Experimente online!


Pode guardar 2 bytes através da remoção dos suportes e colocando b||printf("%d,",n)no interior do for-loop: 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 Obrigado.
Jonathan Frech

@ceilingcat Obrigado; pura equivalência, essa.
Jonathan Frech 15/09

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.