Considere a seguinte matriz:
/www/htdocs/1/sites/lib/abcdedd
/www/htdocs/1/sites/conf/xyz
/www/htdocs/1/sites/conf/abc/def
/www/htdocs/1/sites/htdocs/xyz
/www/htdocs/1/sites/lib2/abcdedd
qual é a maneira mais curta e elegante de detectar o caminho de base comum - neste caso
/www/htdocs/1/sites/
e removê-lo de todos os elementos da matriz?
lib/abcdedd
conf/xyz
conf/abc/def
htdocs/xyz
lib2/abcdedd
Respostas:
Escreva uma função
longest_common_prefix
que receba duas strings como entrada. Em seguida, aplique-o às strings em qualquer ordem para reduzi-las a seu prefixo comum. Por ser associativa e comutativa, a ordem não importa para o resultado.Este é o mesmo que para outras operações binárias como, por exemplo, adição ou maior divisor comum.
fonte
Carregue-os em uma estrutura de dados trie. Começando pelo nó pai, veja qual é a contagem de filhos maior do que um. Depois de encontrar esse nó mágico, basta desmontar a estrutura do nó pai e ter o nó atual como raiz.
fonte
fonte
/usr/lib
e/usr/lib2
deu/usr/lib
como o caminho comum mais longo, em vez de/usr/
). Eu (espero) consertei ambos.Bem, considerando que você pode usar
XOR
nesta situação para encontrar as partes comuns da corda. Sempre que você xou dois bytes iguais, obtém um byte nulo como saída. Portanto, podemos usar isso a nosso favor:Após esse único loop, a
$length
variável será igual à parte de base comum mais longa entre a matriz de strings. Então, podemos extrair a parte comum do primeiro elemento:E aí está. Como uma função:
Note que ele usa mais de uma iteração, mas essas iterações são feitas em bibliotecas, então em linguagens interpretadas isso terá um grande ganho de eficiência ...
Agora, se você quiser apenas caminhos completos, precisamos truncar para o último
/
caractere. Assim:Agora, ele pode cortar excessivamente duas cordas, como
/foo/bar
e/foo/bar/baz
será cortado/foo
. Mas, além de adicionar outra rodada de iteração para determinar se o próximo caractere é um/
ou outro , não consigo ver uma maneira de contornar isso ...fonte
Uma abordagem ingênua seria explodir os caminhos na
/
comparação sucessiva de todos os elementos nas matrizes. Então, por exemplo, o primeiro elemento estaria vazio em todas as matrizes, então ele será removido, o próximo elemento seráwww
, é o mesmo em todos os arrays, então ele será removido, etc.Algo como (
não testado)Depois, você só precisa implodir os elementos
$exploded_paths
novamente:O que me dá:
Isso pode não escalar bem;)
fonte
Ok, não tenho certeza se isso é à prova de balas, mas acho que funciona:
Isso tomará o primeiro valor da matriz como string de referência. Em seguida, ele itera sobre a string de referência e compara cada caractere com o caractere da segunda string na mesma posição. Se um char não corresponder, a string de referência será encurtada para a posição do char e a próxima string será comparada. A função retornará a string correspondente mais curta.
O desempenho depende das cordas fornecidas. Quanto mais cedo a string de referência ficar mais curta, mais rápido o código terminará. Eu realmente não tenho ideia de como colocar isso em uma fórmula.
Descobri que a abordagem da Artefacto para classificar as cordas aumenta o desempenho. Adicionando
antes de o
array_reduce
aumentará significativamente o desempenho.Observe também que isso retornará a substring inicial correspondente mais longa , que é mais versátil, mas não fornecerá o caminho comum . Voce tem que correr
no resultado. E então você pode usar o resultado para remover os valores
que deve dar:
Feedback bem-vindo.
fonte
Você pode remover o prefixo da maneira mais rápida, lendo cada caractere apenas uma vez:
fonte
Isso tem a vantagem de não ter complexidade de tempo linear; entretanto, na maioria dos casos, a classificação definitivamente não será a operação demorando mais.
Basicamente, a parte inteligente (pelo menos não consegui encontrar uma falha nisso) aqui é que depois de classificar você só terá que comparar o primeiro caminho com o último.
fonte
EDITAR Variante do meu método original usando um array_walk para reconstruir o array
EDITAR
A resposta mais eficiente e elegante provavelmente envolve a obtenção de funções e métodos de cada uma das respostas fornecidas
fonte
Gostaria de
explode
usar os valores com base em / e, em seguida, usararray_intersect_assoc
para detectar os elementos comuns e garantir que eles tenham o índice correspondente correto na matriz. A matriz resultante pode ser recombinada para produzir o caminho comum.Isso não foi testado, mas a ideia é que a
$commonPath
matriz sempre contém apenas os elementos do caminho que foram contidos em todas as matrizes de caminho que foram comparadas a ela. Quando o loop está completo, simplesmente o recombinamos com / para obter o verdadeiro$commonPath
Atualização Como apontado por Felix Kling,
array_intersect
não considerarei caminhos que tenham elementos comuns, mas em ordens diferentes ... Para resolver isso, usei emarray_intersect_assoc
vez dearray_intersect
Atualizar o código adicionado para remover o caminho comum (ou tetris it!) Do array também.
fonte
/a/b/c/d
e/d/c/b/a
. Mesmos elementos, caminhos diferentes.O problema pode ser simplificado apenas visto do ângulo de comparação das cordas. Provavelmente, isso é mais rápido do que a divisão de matriz:
fonte
Talvez a portabilidade do algoritmo
os.path.commonprefix(m)
usado pelo Python funcione?Isso é, uh ... algo como
Depois disso, você pode apenas substring cada elemento da lista original com o comprimento do prefixo comum como o deslocamento inicial.
fonte
Vou jogar meu chapéu no ringue ...
Uso:
fonte
Bem, já existem algumas soluções aqui, mas, só porque era divertido:
Resultado:
fonte
Isso funciona bem ... semelhante ao mark baker, mas usa str_replace
fonte
Provavelmente muito ingênuo e noob, mas funciona. Eu usei este algoritmo :
Resultado:
:)
fonte
/www/htdocs/1/sites/conf/
como uma correspondência comum. Além disso, o algoritmo procura substrings começando em qualquer lugar na string, mas para esta pergunta você sabe que pode começar na localização 0, o que torna muito mais simples.