Considere la siguiente 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
¿Cuál es la forma más corta y elegante de detectar la ruta base común ? En este caso
/www/htdocs/1/sites/
y eliminándolo de todos los elementos de la matriz?
lib/abcdedd
conf/xyz
conf/abc/def
htdocs/xyz
lib2/abcdedd
Respuestas:
Escribe una función
longest_common_prefix
que tome dos cadenas como entrada. Luego aplíquelo a las cadenas en cualquier orden para reducirlas a su prefijo común. Dado que es asociativo y conmutativo, el orden no importa para el resultado.Esto es lo mismo que para otras operaciones binarias como, por ejemplo, la suma o el máximo común divisor.
fuente
Cárguelos en una estructura de datos trie. Comenzando desde el nodo padre, vea cuál tiene un hijo más importante que uno. Una vez que encuentre ese nodo mágico, simplemente desmantele la estructura del nodo principal y tenga el nodo actual como raíz.
fuente
fuente
/usr/lib
y/usr/lib2
dio/usr/lib
como la ruta común más larga, en lugar de/usr/
). Yo (con suerte) arreglé ambos.Bueno, considerando que se puede utilizar
XOR
en esta situación para encontrar las partes comunes de la cadena. Cada vez que xo dos bytes que son iguales, obtiene un byte nulo como salida. Entonces podemos usar eso a nuestro favor:Después de ese bucle único, la
$length
variable será igual a la parte base común más larga entre la matriz de cadenas. Luego, podemos extraer la parte común del primer elemento:Y ahí lo tienes. Como una función:
Tenga en cuenta que usa más de una iteración, pero esas iteraciones se realizan en bibliotecas, por lo que en los lenguajes interpretados esto tendrá una gran ganancia de eficiencia ...
Ahora, si solo desea rutas completas, debemos truncar al último
/
carácter. Entonces:Ahora, puede cortar demasiado dos cuerdas como
/foo/bar
y/foo/bar/baz
se cortará/foo
. Pero por debajo de la adición de una nueva ronda de iteración para determinar si el siguiente carácter es o bien/
o al final de la cadena, no puede ver una forma de evitar eso ...fuente
Un enfoque ingenuo sería explotar las rutas en el
/
y comparar sucesivamente todos los elementos de las matrices. Entonces, por ejemplo, el primer elemento estaría vacío en todas las matrices, por lo que se eliminará, el siguiente elemento seráwww
, es el mismo en todas las matrices, por lo que se eliminará, etc.Algo como (
no probado)Luego solo tienes que implosionar los elementos
$exploded_paths
nuevamente:Lo que me da:
Esto podría no escalar bien;)
fuente
Ok, no estoy seguro de que sea a prueba de balas, pero creo que funciona:
Esto tomará el primer valor de la matriz como cadena de referencia. Luego iterará sobre la cadena de referencia y comparará cada carácter con el carácter de la segunda cadena en la misma posición. Si un carácter no coincide, la cadena de referencia se acortará a la posición del carácter y se comparará la siguiente cadena. Entonces, la función devolverá la cadena coincidente más corta.
El rendimiento depende de las cuerdas dadas. Cuanto antes se acorte la cadena de referencia, más rápido finalizará el código. Sin embargo, realmente no tengo ni idea de cómo poner eso en una fórmula.
Descubrí que el enfoque de Artefacto para clasificar las cuerdas aumenta el rendimiento. Añadiendo
antes de
array_reduce
que aumentará significativamente el rendimiento.También tenga en cuenta que esto devolverá la subcadena inicial coincidente más larga , que es más versátil pero no le dará la ruta común . Tienes que correr
en el resultado. Y luego puedes usar el resultado para eliminar los valores
que debería dar:
Comentarios bienvenidos.
fuente
Puede eliminar el prefijo de la manera más rápida, leyendo cada carácter solo una vez:
fuente
Esto tiene la ventaja de no tener una complejidad de tiempo lineal; sin embargo, en la mayoría de los casos, la operación definitivamente no será la que lleve más tiempo.
Básicamente, la parte inteligente (al menos no pude encontrar una falla) aquí es que después de clasificar solo tendrá que comparar la primera ruta con la última.
fuente
EDITAR Variante de mi método original usando un array_walk para reconstruir la matriz
EDITAR
Es probable que la respuesta más eficiente y elegante implique tomar funciones y métodos de cada una de las respuestas proporcionadas
fuente
Yo usaría
explode
los valores basados en / y luego los usaríaarray_intersect_assoc
para detectar los elementos comunes y asegurarme de que tengan el índice correspondiente correcto en la matriz. La matriz resultante podría recombinarse para producir la ruta común.Esto no está probado, pero la idea es que la
$commonPath
matriz solo contenga los elementos de la ruta que han estado contenidos en todas las matrices de ruta que se han comparado con ella. Cuando el ciclo está completo, simplemente lo recombinamos con / para obtener el verdadero$commonPath
Actualización Como señaló Felix Kling,
array_intersect
no consideraré caminos que tengan elementos comunes pero en diferentes órdenes ... Para resolver esto, usé enarray_intersect_assoc
lugar dearray_intersect
Actualización Código agregado para eliminar la ruta común (¡o tetris!) De la matriz también.
fuente
/a/b/c/d
y/d/c/b/a
. Mismos elementos, diferentes caminos.El problema se puede simplificar si solo se ve desde el ángulo de comparación de cuerdas. Esto probablemente sea más rápido que la división de matrices:
fuente
¿Quizás la portabilidad del algoritmo que
os.path.commonprefix(m)
usa Python funcionaría?Eso es, eh ... algo como
Después de eso, puede simplemente subescribir cada elemento de la lista original con la longitud del prefijo común como el desplazamiento inicial.
fuente
Tiraré mi sombrero al ring ...
Uso:
fuente
Bueno, ya hay algunas soluciones aquí pero, solo porque fue divertido:
Salida:
fuente
Esto funciona bien ... similar a Mark Baker pero usa str_replace
fuente
Probablemente demasiado ingenuo y novato, pero funciona. He usado este algoritmo :
Salida:
:)
fuente
/www/htdocs/1/sites/conf/
una coincidencia común. Además, el algoritmo busca subcadenas que comienzan en cualquier lugar de la cadena, pero para esta pregunta, sabe que puede comenzar en la ubicación 0, lo que lo hace mucho más simple.