Kawú
Tengo un conjunto de cuerdas, por ejemplo
my_prefix_what_ever
my_prefix_what_so_ever
my_prefix_doesnt_matter
Simplemente quiero encontrar la porción común más larga de estas cadenas, aquí el prefijo. En lo anterior el resultado debe ser
my_prefix_
Las cuerdas
my_prefix_what_ever
my_prefix_what_so_ever
my_doesnt_matter
debe dar como resultado el prefijo
my_
¿Existe una forma relativamente indolora en Python para determinar el prefijo (sin tener que iterar sobre cada carácter manualmente)?
PD: estoy usando Python 2.6.3.
Ned Batchelder
Nunca reescriba lo que se le proporciona: os.path.commonprefix
hace exactamente esto:
Devuelve el prefijo de la ruta más larga (tomado carácter por carácter) que es un prefijo de todas las rutas de la lista. Si la lista está vacía, devuelve la cadena vacía (
''
). Tenga en cuenta que esto puede devolver rutas no válidas porque funciona con un carácter a la vez.
Para comparar con las otras respuestas, aquí está el código:
# Return the longest prefix of all list elements.
def commonprefix(m):
"Given a list of pathnames, returns the longest common leading component"
if not m: return ''
s1 = min(m)
s2 = max(m)
for i, c in enumerate(s1):
if c != s2[i]:
return s1[:i]
return s1
-
Creo que esto solo puede manejar dos cadenas en m, ¿no? Sin embargo, el comentario dice “todos los elementos de la lista, lo que indica cualquier cantidad de elementos”
– sramij
27 de octubre de 2017 a las 5:43
-
@sramij ¡No exactamente! min () y max () en la cadena es el mínimo lexicográfico y el mínimo como en el diccionario. Entonces, cuando el mínimo y el máximo tienen la misma primera letra, entonces todas las demás palabras entre ellos también deben tener la misma letra, y así sucesivamente.
– Petr
6 de diciembre de 2017 a las 9:12
-
¿Los argumentos deben ser nombres de ruta válidos? ¿Qué pasa si no lo son? La documentación no dice nada, por lo que no estoy tan seguro de que esto pueda usarse para cadenas arbitrarias.
– hohl
10 de agosto de 2018 a las 15:01
-
@hochi Si necesita rutas válidas, consulte la función hermana
os.path.commonpath
. De la documentación: “A diferencia de commonprefix (), esto devuelve una ruta válida”.– AneesAhmed777
22 de abril de 2020 a las 6:34
-
Dígale eso a los entrevistadores de LC (“nunca reescriba lo que se le proporciona”). Como comentario serio y tal vez constructivo, considere no proporcionar el código fuente para esa función ya que a primera vista me perdí la declaración de que ya es parte de
os.path.commonprefix
– José
22 de junio de 2020 a las 22:27
enviarle
Ned Batchelder probablemente tenga razón. Pero por diversión, aquí hay una versión más eficiente de la respuesta de phimuemue usando itertools
.
import itertools
strings = ['my_prefix_what_ever',
'my_prefix_what_so_ever',
'my_prefix_doesnt_matter']
def all_same(x):
return all(x[0] == y for y in x)
char_tuples = itertools.izip(*strings)
prefix_tuples = itertools.takewhile(all_same, char_tuples)
''.join(x[0] for x in prefix_tuples)
Como una afrenta a la legibilidad, aquí hay una versión de una línea 🙂
>>> from itertools import takewhile, izip
>>> ''.join(c[0] for c in takewhile(lambda x: all(x[0] == y for y in x), izip(*strings)))
'my_prefix_'
-
Para Python3, reemplace
itertools.izip(*strings)
conzip(*strings)
.– Regis mayo
14 mayo 2020 a las 10:20
Aquí está mi solución:
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]
prefix_len = len(a[0])
for x in a[1 : ]:
prefix_len = min(prefix_len, len(x))
while not x.startswith(a[0][ : prefix_len]):
prefix_len -= 1
prefix = a[0][ : prefix_len]
La siguiente es una solución funcional, pero probablemente bastante ineficiente.
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]
b = zip(*a)
c = [x[0] for x in b if x==(x[0],)*len(x)]
result = "".join(c)
Para pequeños conjuntos de cuerdas, lo anterior no es ningún problema. Pero para conjuntos más grandes, personalmente codificaría otra solución manual que verifique cada carácter uno tras otro y se detenga cuando haya diferencias.
Algorítmicamente, esto produce el mismo procedimiento, sin embargo, uno podría evitar construir la lista c
.
Solo por curiosidad, descubrí otra forma de hacer esto:
def common_prefix(strings):
if len(strings) == 1:#rule out trivial case
return strings[0]
prefix = strings[0]
for string in strings[1:]:
while string[:len(prefix)] != prefix and prefix:
prefix = prefix[:len(prefix)-1]
if not prefix:
break
return prefix
strings = ["my_prefix_what_ever","my_prefix_what_so_ever","my_prefix_doesnt_matter"]
print common_prefix(strings)
#Prints "my_prefix_"
Como Ned señaló, probablemente sea mejor usar os.path.commonprefix
que es una función bastante elegante.
Ciruela pasa
La segunda línea de esto emplea la función de reducción en cada carácter de las cadenas de entrada. Devuelve una lista de N+1 elementos donde N es la longitud de la cadena de entrada más corta.
Cada elemento en lote es (a) el carácter de entrada, si todo las cadenas de entrada coinciden en esa posición, o (b) Ninguna. lote.index(Ninguno) es la posición del primero Ninguno en lote: la longitud del prefijo común. afuera es ese prefijo común.
val = ["axc", "abc", "abc"]
lot = [reduce(lambda a, b: a if a == b else None, x) for x in zip(*val)] + [None]
out = val[0][:lot.index(None)]
Aquí hay una solución simple y limpia. La idea es usar la función zip() para alinear todos los caracteres colocándolos en una lista de primeros caracteres, lista de segundos caracteres,… lista de caracteres n. Luego itere cada lista para verificar si contienen solo 1 valor.
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]
list = [all(x[i] == x[i+1] for i in range(len(x)-1)) for x in zip(*a)]
print a[0][:list.index(0) if list.count(0) > 0 else len(list)]
salida: mi_prefijo_
-
¡Bienvenido a Stack Overflow! Si bien este fragmento de código puede resolver la pregunta, incluida una explicación de cómo y por qué esto resuelve el problema realmente ayudaría para mejorar la calidad de tu publicación. ¡Recuerde que está respondiendo la pregunta para los lectores en el futuro, no solo para la persona que pregunta ahora! Edite su respuesta para agregar una explicación y dar una indicación de las limitaciones y suposiciones que se aplican.
–Toby Speight
24 de noviembre de 2016 a las 16:11
-
¿Cómo es esto limpio?
– gracias
23 de enero de 2018 a las 22:11
-
como no esta limpio Otras soluciones tienen códigos en bloques. La lógica es lo suficientemente simple como para hacerlo en una sola tarea.
– Patmanizador
7 febrero 2018 a las 20:05
Entonces, en efecto, está pidiendo el subsecuencia común más larga?
– Konrad Rodolfo
16 de julio de 2011 a las 20:18