Sua tarefa de resolver o problema do SLCSC, que consiste em encontrar o menor código possível para resolver o problema de Maior Subseqüência Comum Mais Longa .
A solução válida para o problema LCS para duas ou mais cadeias S 1 , ... S n é qualquer string T de comprimento máximo de tal forma que os personagens de T aparecer em todos S i , na mesma ordem como em T .
Note-se que T não tem que ser uma sub seqüência de S i .
Exemplo
As cadeias axbycz
e xaybzc
têm 8 subsequências comuns de comprimento 3:
abc abz ayc ayz xbc xbz xyc xyz
Qualquer uma dessas seria uma solução válida para o problema do LCS.
Detalhes
Escreva um programa ou uma função que resolva o problema do LCS, conforme explicado acima, cumprindo as seguintes regras:
A entrada consistirá em duas ou mais cadeias contendo apenas letras minúsculas.
Você pode ler essas strings como uma matriz de strings ou uma única string com um delimitador de sua escolha.
Seu código deve gerar qualquer uma das soluções possíveis para o problema, opcionalmente seguida por um avanço de linha ou entre aspas.
Você pode supor que as seqüências de caracteres tenham menos de 1.000 caracteres e que no máximo 20 sejam.
Dentro desses limites, seu código deve funcionar como esperado na teoria (com tempo e memória ilimitados).
Seu código deve concluir os casos de teste combinados da próxima seção em menos de uma hora na minha máquina (Intel Core i7-3770, 16 GiB RAM).
As abordagens que simplesmente iteram sobre todas as subsequências possíveis não estarão em conformidade com o prazo.
O uso de built-ins que trivializam essa tarefa, como
LongestCommonSequence
, não é permitido.
Aplicam-se as regras de código-golfe padrão .
Casos de teste
a
ab
Resultado: a
aaaaxbbbb
bbbbxcccc
ccccxaaaa
Resultado: x
hxbecqibzpyqhosszypghkdddykjfjcajnwfmtfhqcpavqrtoipocijrmqpgzoufkjkyurczxuhkcpehbhpsuieqgjcepuhbpronmlrcgvipvibhuyqndbjbrrzpqbdegmqgjliclcgahxoouqxqpujsyfwbdthteidvigudsuoznykkzskspjufgkhaxorbrdvgodlb
qnnprxqpnafnhekcxljyysobbpyhynvolgtrntqtjpxpchqwgtkpxxvmwwcohxplsailheuzhkbtayvmxnttycdkbdvryjkfhshulptkuarqwuidrnjsydftsyhuueebnrjvkfvhqmyrclehcwethsqzcyfvyohzskvgttggndmdvdgollryqoswviqurrqhiqrqtyrl
Saída: hxbbpyhogntqppcqgkxchpsieuhbncvpuqndbjqmclchqyfttdvgoysuhrrl
ou qualquer outra subsequência comum do mesmo comprimento
riikscwpvsbxrvkpymvbbpmwclizxlhihiubycxmxwviuajdzoonjpkgiuiulbjdpkztsqznhbjhymwzkutmkkkhirryabricvhb
jnrzutfnbqhbaueicsvltalvqoorafnadevojjcsnlftoskhntashydksoheydbeuwlswdzivxnvcrxbgxmquvdnfairsppodznm
kzimnldhqklxyezcuyjaiasaeslicegmnwfavllanoolkhvqkjdvxrsjapqqwnrplcwqginwinktxnkfcuuvoyntrqwwucarfvjg
Saída: icsvllvjnlktywuar
ou qualquer outra subsequência comum do mesmo comprimento
rblartqkfggrjpiipuzzypkycnyckkcqceeujiyy
yfpnedyjtiwrhyuktripdwyvgkblzodeufkelhub
ywcyboxwqkibwvredkrbdsyudkulpvmhixeqxynh
bnxwahbzhwjxkotnvbxrojkkldtsodtjmvdrmbgr
Saída: krkk
ou qualquer outra subsequência comum do mesmo comprimento
bwfscmotshoczmduwaev
coywaaizdaxjovipsmeh
dobzcpoiedenzlfdjpiu
bbhfeqscvvbwkuoxdoge
drqrzwbcpcsvneodelja
Saída: code
ou qualquer outra subsequência comum do mesmo comprimento
nqrualgoedlf
jgqorzglfnpa
fgttvnogldfx
pgostsulyfug
sgnhoyjlnfvr
wdttgkolfkbt
Saída: golf
ou qualquer outra subsequência comum do mesmo comprimento
epopyfuhgowedpiqpgfj
ddxyestqynpwmytxhozm
ptubgzyqqksieoovuezv
tovotqmnhgzttfpywjgr
aomvytkgaijlgqzkljls
vzvxpaixrnprxvenbbuo
syfuwxlpcyontqlmfvib
arxleuomstkjegpduwcx
xgqrxaopouvkvwgbzewn
yggunddigctgpnuztzai
izroomywwztdymqszsuo
kiawjnxjncdtufhkrjsp
Saída: a sequência vazia
fonte
Respostas:
CJam, 31
Experimente online
9 bytes jogados graças a Dennis!
Explicação:
Esse algoritmo tenta todos os caracteres possíveis para a primeira posição da subsequência, substitui cada sequência pela substring após a primeira ocorrência desse caractere e, em seguida, chama-se recursivamente (com memorização).
fonte
Python -
665644Níveis de indentação:
O código define uma função
o
, que pega uma lista de cadeias como argumentos e retorna um dos LCSs para as cadeias.Código do teste:
Tempo necessário para executar os testes no meu computador:
fonte
(n+1)
, você pode substituí-lo por-~n
para salvar 2 bytes em cada caso. Além disso, em qualquer lugar que você usemap
com alambda
, considere usar a compreensão da lista. Por exemplo,map(lambda x:x-1,z)
pode ser reduzido por três bytes, alterando-o para[~-x for x in z]
.r,x=r+(j+1)*x,x*(l[k]+1)
pode ser reduzido parar+=(j+1)*x;x*=(l[k]+1)
. Além disso, você não precisa,u=...
porqueu
é usado apenas em um lugar. Apenas substitua esse código pela letrau
.Pitão,
59585535 bytesCorte 20 bytes graças a @isaacg!
Versão de 55 bytes:
Corte 3 bytes alterando
.U@bZ
para@F
(o operador fold).Versão de 58 bytes:
Corte um byte alterando o formato de uma condicional booleana.
Versão de 59 bytes:
Isso foi difícil! O Python continuava em falha! Tenho certeza de que era algum tipo de bug, mas não consegui um caso de teste mínimo. Ah bem.
Baseei o algoritmo neste . O que é bom, exceto que aquele foi projetado para apenas duas seqüências de caracteres. Eu tive que ajustá-lo um pouco para fazer funcionar mais. Em seguida, o último caso de teste estava demorando muito, então tive que adicionar uma verificação adicional para sair da recursão se não houver caracteres mais comuns.
É bem lento, mas deve levar menos de uma hora (espero). Estou testando no meu Core i3 com 6 GB de RAM, portanto, o seu Core i7 de 16 GB deve passar por isso. :)
Eu também aproveitei as funções de memorização automática do Pyth para torná-lo um pouco mais rápido.
EDIT : @Dennis disse que passa!
Para testá-lo, adicione a seguinte linha:
e forneça uma lista de strings via entrada padrão (por exemplo
['a', 'ab']
).Explicação para a versão de 35 bytes:
WIP.
Explicação para a versão de 55 bytes:
fonte
C,
618564 bytesE aqui está desvendado, por "legibilidade":
Senhoras e senhores, cometi um erro horrível. É usado para ser mais bonita ... e Goto-less ... Pelo menos agora ele é rápido .
Definimos uma função recursiva
L
que recebe como entrada uma matrizs
de matrizes de caracteres e o númeron
de strings. A função gera a string resultante para stdout e, aliás, retorna o tamanho em caracteres dessa string.A abordagem
Embora o código seja complicado, a estratégia aqui não é muito complexa. Começamos com um algoritmo recursivo bastante ingênuo, que descreverei com pseudocódigo:
Agora, esse algoritmo por si só é bastante atroz (mas pode caber em torno de ~ 230 bytes, eu descobri). Não é assim que se obtém resultados rápidos. Esse algoritmo escala incrivelmente mal com o comprimento da string. Este algoritmo faz , no entanto, escala razoavelmente bem com maior número de cordas. O último caso de teste seria resolvido virtualmente instantaneamente, já que nenhuma sequência de caracteres
s
possui caracteresc
em comum. Foram implementados dois truques principais que resultaram em um incrível aumento de velocidade:A cada chamada para
L
, verifique se recebemos essa mesma entrada antes. Como, na prática, as informações são passadas através de ponteiros para o mesmo conjunto de strings, na verdade não precisamos comparar strings, apenas locais, o que é ótimo. Se descobrimos que obtivemos essas informações antes, não há necessidade de executar os cálculos (na maioria das vezes, mas obter resultados torna isso um pouco mais complicado) e podemos nos safar apenas retornando o comprimento. Se não encontrarmos uma correspondência, salve esse conjunto de entrada / saída para comparar com chamadas futuras. No código C, o segundofor
loop tenta encontrar correspondências para a entrada. Os ponteiros de entrada conhecidos são salvosR
e os valores correspondentes de comprimento e saída de caracteres são armazenados emA
. Esse plano teve um efeito drástico no tempo de execução, especialmente com seqüências mais longas.Toda vez que encontramos os locais
c
ems
, há uma chance de sabermos logo de cara que o que descobrimos não é o ideal. Se todos os locaisc
aparecerem após algum local conhecido de outra letra, sabemos automaticamente que issoc
não leva a uma substring ideal, porque você pode colocar mais uma letra nela. Isso significa que, por um pequeno custo, podemos remover várias centenas de chamadasL
para cadeias grandes. No código C acima,y
é um conjunto de sinalizadores se soubermos automaticamente que esse caractere leva a uma sequência subótima ez
é um conjunto de sinalizadores se encontrarmos um caractere que tenha aparências exclusivamente anteriores a qualquer outro caractere conhecido. As primeiras aparências atuais de caracteres são armazenadas emx
. A implementação atual dessa idéia é um pouco confusa, mas quase dobrou o desempenho em muitos casos.Com essas duas idéias, o que não terminou em uma hora agora levou cerca de 0,015 segundos.
Provavelmente existem muito mais pequenos truques que podem acelerar o desempenho, mas nesse momento comecei a me preocupar com minha capacidade de jogar tudo. Ainda não estou contente com o golfe, então provavelmente voltarei a isso mais tarde!
Horários
Aqui estão alguns códigos de teste, que eu convido você a experimentar online :
Executei os casos de teste do OP em um laptop equipado com um chip Intel Core i7 de 1,7 GHz, com uma configuração de otimização de
-Ofast
. A simulação relatou um pico de 712 KB necessário. Aqui está um exemplo de execução de cada caso de teste, com tempos:No golfe, atingi o desempenho de maneira bastante significativa e, como as pessoas pareciam gostar da velocidade bruta (0,013624 s para concluir todos os casos de teste combinados) da minha solução anterior de 618 bytes, deixarei aqui como referência:
O algoritmo em si é inalterado, mas o novo código baseia-se em divisões e em algumas operações bit a bit mais complicadas que acabam atrasando tudo.
fonte
best_found
? É mencionado apenas duas vezes, uma quando é usada na condicional e a outra quando é redefinida.best_found
está definido comobest_length + current_depth
. Você provavelmente deve mencionar isso na explicação!best_found
é um número inteiro global que descreve o comprimento da substring comum mais longa encontrada em qualquer ponto da execução. Vou colocar isso na explicação!Python 2, 285
Código:
Uso:
Explicação:
Esta é uma função recursiva.
s
é o personagem que estamos procurando.a
contém uma lista de seqüências de caracteres fatiadas depoiss
.b
contém uma lista de strings intocadas ainda.f
retorna a string comum mais longa.A primeira condição verifica se terminamos de examinar todas as strings. Nesse caso, significa
s
caractere comum e retornas
e procura por caracteres mais comuns.A segunda condição verifica se não começamos a passar por nenhuma sequência, o que significa que nem sequer temos um caractere (
a==[]
é equivalente as==''
). Nesse caso, verificamos cada caractere da primeira stringb
.A última linha move a primeira sequência
b
paraa
, localizando cada ocorrências
dessa sequência.Na primeira chamada,
s
deve haver uma sequência vazia.a
deve estar em uma lista vazia eb
deve conter todas as cadeias.fonte
f(b,s='',a=[])
.