Considérez le tableau suivant:
/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
quel est le moyen le plus court et le plus élégant de détecter le chemin de base commun - dans ce cas
/www/htdocs/1/sites/
et le supprimer de tous les éléments du tableau?
lib/abcdedd
conf/xyz
conf/abc/def
htdocs/xyz
lib2/abcdedd
Réponses:
Ecrire une fonction
longest_common_prefix
qui prend deux chaînes en entrée. Appliquez-le ensuite aux chaînes dans n'importe quel ordre pour les réduire à leur préfixe commun. Puisqu'il est associatif et commutatif, l'ordre n'a pas d'importance pour le résultat.C'est la même chose que pour d'autres opérations binaires comme par exemple l'addition ou le plus grand diviseur commun.
la source
Chargez-les dans une structure de données trie. À partir du nœud parent, voyez lequel des enfants compte plus d'un. Une fois que vous avez trouvé ce nœud magique, démontez simplement la structure du nœud parent et ayez le nœud actuel en tant que racine.
la source
la source
/usr/lib
et/usr/lib2
il a donné/usr/lib
comme chemin commun le plus long, plutôt que/usr/
). J'ai (espérons-le) corrigé les deux.Eh bien, étant donné que vous pouvez utiliser
XOR
dans cette situation pour trouver les parties communes de la chaîne. Chaque fois que vous xor deux octets identiques, vous obtenez un octet nul en sortie. Nous pouvons donc utiliser cela à notre avantage:Après cette boucle unique, la
$length
variable sera égale au plus long départ de base commun entre le tableau de chaînes. Ensuite, nous pouvons extraire la partie commune du premier élément:Et voila. En tant que fonction:
Notez qu'il utilise plus d'une itération, mais ces itérations sont effectuées dans des bibliothèques, donc dans les langages interprétés, cela aura un énorme gain d'efficacité ...
Maintenant, si vous ne voulez que des chemins complets, nous devons tronquer au dernier
/
caractère. Alors:Maintenant, il peut trop couper deux chaînes telles que
/foo/bar
et/foo/bar/baz
seront coupées/foo
. Mais à moins d'ajouter un autre tour d'itération pour déterminer si le prochain caractère est l'un/
ou l' autre ou la fin de la chaîne, je ne vois pas de moyen de contourner cela ...la source
Une approche naïve consisterait à faire exploser les chemins au niveau de
/
et à comparer successivement chaque élément des tableaux. Ainsi, par exemple, le premier élément serait vide dans tous les tableaux, il sera donc supprimé, le prochain élément serawww
, il est le même dans tous les tableaux, il est donc supprimé, etc.Quelque chose comme (
non testé)Ensuite, il vous suffit d'imploser à
$exploded_paths
nouveau les éléments :Ce qui me donne:
Cela pourrait ne pas bien évoluer;)
la source
Ok, je ne suis pas sûr que ce soit à l'épreuve des balles, mais je pense que cela fonctionne:
Cela prendra la première valeur du tableau comme chaîne de référence. Ensuite, il itérera sur la chaîne de référence et comparera chaque caractère avec le caractère de la deuxième chaîne à la même position. Si un caractère ne correspond pas, la chaîne de référence sera raccourcie à la position du caractère et la chaîne suivante est comparée. La fonction renverra alors la chaîne correspondante la plus courte.
Les performances dépendent des chaînes données. Plus la chaîne de référence raccourcit tôt, plus le code se terminera rapidement. Je ne sais vraiment pas comment mettre cela dans une formule.
J'ai trouvé que l'approche d'Artefacto pour trier les cordes augmente les performances. Ajouter
avant le
array_reduce
augmentera considérablement les performances.Notez également que cela renverra la plus longue sous - chaîne initiale correspondante , qui est plus polyvalente mais ne vous donnera pas le chemin commun . Tu dois courir
sur le résultat. Et puis vous pouvez utiliser le résultat pour supprimer les valeurs
ce qui devrait donner:
Vos commentaires sont les bienvenus.
la source
Vous pouvez supprimer le préfixe de la manière la plus rapide, en ne lisant chaque caractère qu'une seule fois:
la source
Cela présente l'avantage de ne pas avoir de complexité temporelle linéaire; cependant, dans la plupart des cas, le tri ne sera certainement pas l'opération qui prendra plus de temps.
Fondamentalement, la partie intelligente (du moins je n'ai pas trouvé de défaut) ici est qu'après le tri, vous n'aurez qu'à comparer le premier chemin avec le dernier.
la source
EDIT Variante de ma méthode originale utilisant un array_walk pour reconstruire le tableau
ÉDITER
La réponse la plus efficace et la plus élégante impliquera probablement de prendre des fonctions et des méthodes de chacune des réponses fournies
la source
Je voudrais
explode
les valeurs basées sur le /, puis les utiliserarray_intersect_assoc
pour détecter les éléments communs et m'assurer qu'ils ont le bon index correspondant dans le tableau. Le tableau résultant pourrait être recombiné pour produire le chemin commun.Ceci n'est pas testé, mais l'idée est que le
$commonPath
tableau ne contient que les éléments du chemin qui ont été contenus dans tous les tableaux de chemins qui ont été comparés avec lui. Lorsque la boucle est terminée, nous la recombinons simplement avec / pour obtenir le vrai$commonPath
Mise à jour Comme l'a souligné Felix Kling,
array_intersect
ne considérera pas les chemins qui ont des éléments communs mais dans des ordres différents ... Pour résoudre cela, j'ai utilisé à laarray_intersect_assoc
place dearray_intersect
Mise à jour Ajout du code pour supprimer le chemin commun (ou tetris it!) Du tableau également.
la source
/a/b/c/d
et/d/c/b/a
. Mêmes éléments, chemins différents.Le problème peut être simplifié s'il est simplement vu sous l'angle de comparaison des chaînes. C'est probablement plus rapide que le fractionnement de tableau:
la source
Peut-être que le portage de l'algorithme
os.path.commonprefix(m)
utilisé par Python fonctionnerait?C'est, euh ... quelque chose comme
Après cela, vous pouvez simplement sous-chaque élément de la liste d'origine avec la longueur du préfixe commun comme décalage de départ.
la source
Je jetterai mon chapeau dans le ring…
Usage:
la source
Eh bien, il y a déjà des solutions ici mais, juste parce que c'était amusant:
Production:
la source
Cela fonctionne bien ... similaire à Mark Baker mais utilise str_replace
la source
Probablement trop naïf et noobish mais ça marche. J'ai utilisé cet algorithme :
Production:
:)
la source
/www/htdocs/1/sites/conf/
une correspondance commune. En outre, l'algorithme recherche des sous-chaînes commençant n'importe où dans la chaîne, mais pour cette question, vous savez que vous pouvez commencer à l'emplacement 0, ce qui le rend beaucoup plus simple.