Essa tarefa é gerar o caminho mais curto para um arquivo, após a expansão glob.
O que é shell globbing? Na maioria dos shells, você pode usar o *
caractere em um caminho para representar qualquer caractere na posição. Por exemplo, se o diretório foo
contiver arquivos bar
baz
e asdf
, foo/b*
será expandido para foo/bar foo/baz
.
Agora, digamos que o diretório atual contenha um arquivo chamado ihavealongname
e nada mais. Se eu quiser referenciar esse arquivo, digite *
: o que representará apenas esse arquivo, em vez de digitar o nome completo.
Se o diretório também contiver um arquivo chamado ialsohavealongname
, não posso fazer isso *
, pois corresponderá aos dois arquivos. O que eu teria que fazer, pelo menos ih*
,.
O *
padrão também funciona para diretórios correspondentes acima do arquivo que estou procurando. Se há apenas dois diretórios foo
e bar
, mas foo
contém apenas um arquivo baz
e bar
contém arquivo asdf
, posso corresponder foo/baz
com */baz
. Ou, ainda mais concisa */b*
,. Se bar
estivesse vazio, */*
funcionaria.
Sua tarefa: dada uma série de caminhos que representam o "diretório atual" e um único caminho de destino, produza a menor seqüência possível que seria expandida para apenas o caminho de destino após a expansão * s.
O caminho de destino pode ser usado como sua própria sequência, como um índice na matriz de caminhos, como o primeiro item na matriz de caminhos transmitidos ou alguma outra maneira conveniente que não seja codificada. Pergunte nos comentários se não tiver certeza.
O caminho de destino é garantido para estar presente no "diretório atual".
Você pode supor que todos os caminhos contenham apenas ASCII alfanuméricos /
. Você pode usar como caminhos de entrada enraizados (inicie com /
) ou relativos (não inicie com /
).
Se houver várias possibilidades igualmente curtas, retorne uma ou todas elas.
Isso é código-golfe , o menor número de bytes vence!
Casos de teste , graças a Kevin Cruijssen .
fonte
*
,?
,[
etc? Seria talvez seja mais fácil se você acabou de afirmar que os nomes de arquivos e diretórios são alfanuméricos*
e executar o perlglob
para obter todos os nomes de arquivos que possam ser relevantes (por exemplo,foo/bar/baz
torna-se*/*/*
). Depois disso, torna-se um desafio no processamento de strings. E esse desafio já é difícil o suficiente. Eu acho que esse desafio seria mais limpo como "dada uma lista de/
caminhos alfanuméricos (e ) relativos, encontre a menor glob que corresponda apenas a esse caminho de destino existente"a*f
para selecionarazzf
a partir deazzf
,azzg
,bzzf
. Estenda à vontade paraa*b*c
etc.Respostas:
Perl 5 ,
136107102 bytesInclui
+2
paran0
Dê uma lista de arquivos no STDIN. O primeiro é considerado o arquivo de destino
Apenas o código sem tornar as novas linhas literais:
Falha intencionalmente após a impressão da solução.
Ainda parece muito longo (o uso
$a
e1/0
é muito complicado), mas é um começo e deve ser razoavelmente eficiente.Experimente online!
Como funciona
O programa cria globs candidatos, aumentando-os de trás para frente, começando com a string vazia. Ele faz isso de uma primeira forma amplitude, de modo primeiras bolhas de tamanho 0 são experimentadas (apenas ``), em seguida, o comprimento 1 (como
t
,i
,*
), próximo comprimento 2 (comofb
,i*
,*g
,**
), próximo comprimento 3 e assim por diante até que um glob é encontrado que corresponde apenas ao primeiro caminho. Esse será o menor glob que resolve o problema (outros do mesmo tamanho podem existir).Os globs de comprimento
n+1
são gerados a partir dos globs de comprimenton
, precedendo cada caractere da lista de caminhos e também*
na frente de cada glob de comprimenton
. Assim, por exemplo, comprimento 3 glob*i*
contribuirá comprimento 4 globsf*i*
,o*i*
,o*i*
,/*i*
,b*i*
...s*i*
,t*i*
e, finalmente**i*
. Observe que todos os caracteres da lista de caminhos de entrada são anexados, mesmo que apareçam várias vezes ou não façam sentido, pois levam a algo que nunca pode corresponder.Fazer isso ingenuamente levaria a uma explosão combinatória. É por isso que todo glob candidato é avaliado quanto à sua utilidade, determinando em quais pontos nos caminhos ele poderia corresponder se o glob fosse usado no final de um glob completo. Eu faço isso inserindo um
;
em cada local onde uma correspondência é possível. Por exemplo, para o globt*
, receberei a string:Isso representa o "poder de distinção" da glob. Todo globo que tem exatamente o mesmo poder de distinção é igualmente bom. Se você substituí-los um pelo outro no final de um glob completo, todos corresponderão exatamente aos mesmos caminhos. Então você também pode usar o menor.
Então, ao considerar os
n
globs de comprimento , primeiro olho para seu poder de distinção. Se já foi visto antes, havia outro globo de comprimenton
ou menor que já era considerado e expandido, portanto esse globo é inútil e é podado. Isso, por exemplo, livrar-se-á de candidatos,**i*
já que o mesmo poder de distinção já foi visto*i*
. Também apaga candidatos impossíveis, comof*i*
a cadeia de caracteres distintiva não terá;
e seja apenas a lista original de caminhos. Somente o primeiro globo impossível será aceito, todos os outros serão vistos como tendo o mesmo poder de distinção e serão podados. E mesmo esse primeiro não será realmente expandido, pois todas as expansões ainda são impossíveis e serão removidas quando consideradas. Simularmentein*
serão podados pori*
etc.O exposto acima leva a uma poda muito agressiva e, portanto, o programa é capaz de lidar com casos complexos em um tempo muito curto. Uma grande ineficiência, no entanto, é que ele prefixa os globs candidatos com todos os caracteres possíveis, não apenas aqueles imediatamente antes de um
;
caminho no destino, parte da string distintiva. Todos os personagens adicionados que não estão na frente de um;
não são problema, pois levam a um globo impossível que será removido quando for considerado, mas que ainda deixa os personagens logo antes;
nos outros caminhos. Portanto, no final, o programa também cria globs que poderão corresponder a qualquer combinação dos caminhos fornecidos. Não tem idéia de que deveria se concentrar no primeiro caminho.Agora considere uma solução para o problema. No exemplo dado que poderia ser
*/*er/t
. Isso fornece a seguinte cadeia de caracteres distintiva:Reconheço uma solução tendo um
;
na primeira posição (para que ele corresponda ao primeiro caminho) e não tendo um;
no início de qualquer outro caminho (para que os outros não correspondam)Com o algoritmo explicado, agora chego ao programa atual:
Os globos candidatos estarão em um array
@a
que eu faço um loop usando a variável$a
que contém o glob atualmente em consideração. Em vez de*
no glob, no entanto, eu o usarei\w*
,$a
na verdade, é um regex em vez de um glob. Vou abusar de uma estranheza do loop perl for que você pode acrescentar elementos ao array que está sendo repetido enquanto o loop estiver em execução e esses novos elementos serão captados no loop. Como ao gerar osn+1
globs de comprimento, todos os globs de comprimenton
já estão no array,@a
isso é a amplitude primeiro.Devido à
-n0
opção (loop implícito em toda a entrada), a lista de caminhos está em$_
uma grande cadeia de caracteres com cada caminho terminado com uma nova linhaDentro do
{ }
nós temos:Opa, acabei de destruir
$_
e vou precisar para o próximo loop. Envolva o código de trabalho real dentroIsso corresponde à string vazia no início
$_
e permite executar o código para determinar com o que ela é substituída. Se eu garantir que esse código seja avaliado como a sequência vazia$_
, no final, permanecerá inalterado, mesmo que eu mude$_
durantecode
.Voltando a pouco depois de eu ter substituído
$_
a string distintiva:Isto é como:
//
em perl é'defined or
. É como um curto-circuito emor
que o segundo argumento é avaliado apenas se o primeiro forundef
. E pode ser combinado com uma tarefa, como+=
em outros idiomas. Portanto, se eles digitarem$_
hash%seen
isundef
(que é o que você obtém ao acessar um elemento não existente), execute a expressão e atribua-a como valor à chave$_
. Portanto, se eu garantirexpression
que não retorne,undef
isso significa basicamente "avaliar expressão se e somente se for a primeira vez que vemos essa sequência distinta". E, como$_
é garantido que contém um\n
, é realmente seguro abusar do hash global perl para armazenar as seqüências distintas, portanto, em$$_
vez de$seen{$_}
Para o que
expression
eu uso:Basicamente "Para cada caractere (exceto nova linha) na cadeia de caracteres distintiva e também o
*
prefixo ao globo atual e empurre isso na matriz de globs candidatos". Execução que uso\w*
para*
obter um regex válido (eu poderia usar em''
vez de""
me livrar de uma barra invertida, mas não conseguia executar meu código na linha de comando). Observe que isso também pega os;
e os adiciona aos globos candidatos, mas quando mais tarde testá-los no restaurado,$_
que não possui,;
será novamente um globo impossível e será podado.Observe que
/^;/>/\n;/
possui um valor equivalente à sequência vazia, caso uma solução ainda não tenha sido encontrada, portanto, ela funcionará como uma sequência de substituição vazia e$_
será restauradafonte
-E
Ativa o último nível de idioma. Você precisa de pelo menos perl5.10.0
para poder usarsay
. Então coloqueuse 5.10.0;
na seção de cabeçalho e ele funcionará. Opções para definir a contagem do nível do idioma como livre de qualquer maneira, mesmo que você também não possa fazê-lo usando-E
. Na verdade todas as opções contam como livre hoje (então eu nem sequer tem que contarn0
), mas considero que demasiado branda para perl1/
solução é válida! Eu preciso lembrar que também ...Java 10,
854824796738728703688655652647624 bytesQue bagunça .. Esse certamente não é um desafio fácil em Java.
Definitivamente pode ser jogado por algumas centenas de bytes, mas estou feliz que finalmente esteja funcionando agora.Te disse. :)-5 bytes graças a @ceilingcat .
-23 bytes alternando do Java 8 para o Java 10
Entrada como uma sequência de caminhos de arquivo de cadeia de caracteres (com diretórios como itens separados e todos os itens que contêm um líder
/
) e uma sequência de caracteres com caminho de arquivo de entrada para agrupar.Explicação:
Experimente online. (Os casos de teste com
ialsohavealongname
/ihavealongnameaswell
são ligeiramente reduzidos es.add(x.replaceAll("~+","\\*"));
foram substituídos por{s.remove(x);s.add(x.replaceAll("~+","\\*"));}
5-10 segundos no TIO, em vez de atingir o tempo limite após mais de 60 segundos.)Explicação geral adicional:
Exemplo: Vamos tomar
/foo, /foo/bar, /foo/barber, /foo/bar/test, /foo/barber/test, /foo/barber/testing, /foo/barber/coding, /foo/test
como caminhos de arquivo efoo/bar/test
como caminho de arquivo de entrada para grop.1) Começo dividindo a entrada do caminho do
/
arquivo e giro todos os agrupamentos de arquivos dessas palavras separadas:2) Gerei todas as permutações com estas palavras na mesma ordem (reaplicando o
/
intermediário e o frontal):3) Em seguida, percorro os itens nesta lista acima e valido se ele corresponde apenas a um único caminho de arquivo na matriz de entrada de caminhos de arquivo. (Faço isso verificando duas coisas: a quantidade de barras é a mesma e corresponde ao regex em que todos
*
são substituídos.*
.)Se isso ocorrer: mantenha o (primeiro) mais curto, o que retornamos no final.
fonte
>>>
? Eu sei que>>
é a mudança certa bit a bit.>>>
atua da mesma forma que>>
. Mas para números inteiros negativos, altera o bit de paridade para 0 (você pode ver alguns exemplos aqui na seção " >> vs >>> " ).-1>>>1
é apenas uma variante mais curta deInteger.MAX_VALUE
(e1<<31
seriaInteger.MIN_VALUE
).