Regex simples mais curto que corresponde a uma palavra binária


20

Tarefa

Defina uma regex simples como uma expressão regular não vazia que consiste apenas em

  • caracteres 0e 1,
  • agrupar parênteses (e ),
  • um ou mais quantificadores de repetição +.

Dada uma sequência não vazia de 0s e 1s, seu programa deve encontrar a regex simples mais curta que corresponda à sequência de entrada completa . (Ou seja, ao corresponder a uma regex simples, finja que ela é reservada por ^ e  $.) Se houver várias regexes mais curtas, imprima uma ou todas elas.

, de modo que o menor envio (em bytes) vence.

Casos de teste

1 -> 1
00 -> 00 or 0+
010 -> 010
1110 -> 1+0
01010 -> 01010
0101010 -> 0(10)+ or (01)+0
011111 -> 01+
10110110 -> (1+0)+
01100110 -> (0110)+ or (01+0)+
010010010 -> (010)+
111100111 -> 1+001+ or 1+0+1+
00000101010 -> 0+(10)+ or (0+1)+0
1010110001 -> 1(0+1+)+ or (1+0+)+1

3
Você deve esclarecer que deseja que escrevamos um programa que grave o regex, e não o regex. Mas isso parece interessante.
Gcampbell 7/06

1
No meu teste, 01100110é um caso interessante ... um algoritmo ingênuo escreveria 01+0+1+0ou (0+1+)+0não seria o ideal.
7116 Neil

Respostas:


2

Pitão, 20 bytes

hf.x}z:zT1Zy*4"()01+

Isso leva aproximadamente 30 segundos para ser executado, portanto, ele precisa ser executado offline.

Explicação:

hf.x}z:zT1Zy*4"()01+
                        Implicit: z is the input string.
              "()01+    "()01+"
            *4          Repeated 4 times
           y            All subsequences in length order
hf                      Output the first one such that
      :zT1              Form all regex matches of z with the candidate string
    }z                  Check if the input is one of the strings
  .x      Z             Discard errors

Não tenho certeza absoluta de que toda seqüência mais curta seja uma subsequência de "() 01+" * 4, mas 4 pode ser aumentado para 9 sem custo de bytes, se necessário.


9

JavaScript (ES6), 488 341 bytes

s=>[s.replace(/(.)\1+/g,'$1+'),...[...Array(60)].map((_,i)=>`(${(i+4).toString(2).slice(1)})+`),...[...Array(1536)].map((_,i)=>`${i>>10?(i>>8&1)+(i&2?'+':''):''}(${i&1}${i&4?i>>4&1:i&16?'+':''}${i&8?''+(i>>7&1)+(i&64?i>>5&1:i&32?'+':''):''})+${i&512?(i>>8&1)+(i&2?'+':''):''}`)].filter(r=>s.match(`^${r}$`)).sort((a,b)=>a.length-b.length)[0]

Explicação: Como seis regexes podem expressar todas as palavras binárias possíveis e os dois mais longos têm nove caracteres, basta verificar essas e todas as regexes mais curtas. Um candidato é obviamente a string com "codificação do comprimento da execução" (ou seja, todas as execuções de dígitos substituídas por +s apropriadas ), mas também as strings com um conjunto de ()s precisam ser verificadas. Eu gero 1596 dessas expressões regulares (isso inclui duplicatas e expressões inúteis, mas elas serão eliminadas) e testo todas as 1597 para ver qual é a correspondência mais curta. As regexes geradas se enquadram em dois tipos: \(\d{2,5}\)\+(60 regexes) e (\d\+?)?\(\d[\d+]?(\d[\d+]?)?\)(\d\+?)?(1536 regexes, pois evito gerar regexes com os dígitos inicial e posterior).


@LeakyNun Originalmente, eu pensava que havia 4 expressões regulares de comprimento 9, mas isso está obviamente incorreto, por isso esclarei minha explicação.
Neil

2

Pitão - 31 30 29 bytes

Força bruta! Provavelmente pode jogar um pouco no iterador.

 f=+Yf.x:zjY"^$")Z^"10+()"T1Y

Suíte de teste .


1

Ruby, 109 bytes

É a abordagem chata da força bruta. Funciona porque nenhum regex precisa ter mais de 9 caracteres (como observa Neil) e nenhum caractere individual precisa ser repetido mais de 4 vezes (tentar isso '01()+'.chars*9deixou minha CPU infeliz).

10.times{|i|('01()+'.chars*4).combination(i).map{|s|begin
/^#{s*''}$/=~$*[0]&&[puts(s*''),exit]
rescue
end}}
$ for word in `grep -Po '^\S+' test_cases.txt`; do nice -n20 ruby sre.rb $word; done
1
0+
010
1+0
01010
0(10)+
01+
(1+0)+
(01+0)+
(010)+
1+0+1+
0+(10)+
1(0+1+)+

1

Python 3, 186 bytes

Estou investigando se existe uma abordagem para esse problema além da força bruta, mas aqui está uma solução de força bruta do Python por enquanto.

import re,itertools
def a(b):
 for z in range(10):
  for i in itertools.combinations("01()+"*4,z):
   j=''.join(i)
   try:
    if re.fullmatch(j,b)and len(j)<=len(b):return j
   except:1
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.