Directorio de artículos
Entendido completamente
En el algoritmo de coincidencia de cadenas, el algoritmo KMP puede lograr una complejidad casi O(N), la clave es eliminar el retroceso del puntero principal, lo que puede ahorrar mucho tiempo.
Por ejemplo, si desea hacer coincidir abcdabce
y hacer abce
coincidir, el algoritmo de fuerza bruta se muestra en la siguiente tabla, cada vez que necesita comparar 4 caracteres, un total de 5 veces.
un | b | C | d | un | b | C | mi | |
---|---|---|---|---|---|---|---|---|
1 | un | b | C | mi | ||||
2 | un | b | C | mi | ||||
3 | un | b | C | mi | ||||
4 | un | b | C | mi | ||||
√ | un | b | C | mi |
Sin embargo, podemos ver de un vistazo que esto d
no abce
está en absoluto, por lo que si podemos almacenar alguna otra información, tal vez podamos omitir esto de inmediato d
.
un | b | C | d | un | b | C | mi | |
---|---|---|---|---|---|---|---|---|
1 | un | b | C | mi | ||||
√ | un | b | C | mi |
Pero a veces no puedes saltar más, por ejemplo, si quieres abcabcabc
hacer coincidir cab
, una mejor solución es más o menos la siguiente
un | b | C | un | b | C | un | b | C | |
---|---|---|---|---|---|---|---|---|---|
1 | C | un | b | ||||||
√ | C | un | b |
Entonces, el quid de la pregunta es, ¿por qué el caso anterior se puede omitir directamente d
y el caso siguiente solo se puede omitir exactamente dos?
Para resumir, encontramos dos reglas. Deje que txt sea un texto largo, necesita encontrar una cadena en txt, y deje que el carácter de la comparación actual sea ch, hay dos reglas simples
ch no está en str | str saltar esta ch |
ch pasa a ser str[0] | str se transfiere a la posición de este ch |
Entonces, si ch está en str, pero no en str[0], ¿qué se debe considerar?
Por supuesto, no se puede omitir directamente, porque puede haber secuencias repetidas en str, como la coincidencia abababc
de ellos ababc
, entonces la mejor solución debería ser
un | b | un | b | un | b | C | |
---|---|---|---|---|---|---|---|
1 | un | b | un | b | C | ||
√ | un | b | un | b | C |
Es decir, para ababc
este tipo de cadenas, debido a a
las diferentes posiciones, cuando obtengamos una nueva a
, se tomará una decisión diferente.
En la siguiente figura, el círculo representa la coincidencia actual, la flecha representa un nuevo carácter y la flecha apunta a la siguiente posición de salto. Un signo de exclamación significa que no es un carácter determinado.
Ver aquí no es un sentimiento de comprensión, esta es la llamada máquina de estado, y el proceso de construcción de esta máquina de estado no tiene nada que ver con txt. En otras palabras, solo haga una auto-coincidencia en str antes de hacer coincidir txt, y puede obtener una máquina de estado como esta.
El estado en la máquina de estados en realidad representa la posición del puntero en la cadena que se va a emparejar. Volver a a significa que el puntero apunta a 0; al avanzar, el puntero aumenta en 1; cuando abab recibe una a y vuelve al paso de aba, significa que el puntero retrocede de 5 a 3, como se muestra en la siguiente tabla.
un | lejos | aba | abab | ababc | |
---|---|---|---|---|---|
un | 0 | 0 | 0 | 3 | |
b | 0 | 0 | 0 | 0 | |
C | 0 | 0 | 0 | 0 | éxito |
otro | 0 | 0 | 0 | 0 |
¿Te diste cuenta de repente de que la llamada máquina de estado es en realidad una matriz?
Lo siguiente que tenemos que hacer es generar esta matriz de estado.
implementación aproximada
O considere encontrar str de txt, y el primer paso es establecer la matriz de estado de str
test = "ababcdabadc" #python中str是关键字,所以改个名
length = len(test)
#创建用于存储状态的字典
status = {
s:[0 for _ in range(length)] for s in set(test)}
for ch in status:
for i in range(length):
for j in range(i+1):
if test[i-j:i]+ch == test[0:j+1]:
status[ch][i] = j+1
conseguir
b [0, 2, 0, 4, 0, 0, 0, 8, 0, 4, 0]
d [0, 0, 0, 0, 0, 6, 0, 0, 0, 10, 0]
c [0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 11]
a [1, 1, 3, 1, 3, 1, 7, 1, 9, 1, 1]
Para una cadena ababcdabadc
, el puntero inicial es 0. Si hay uno en este momento a
, el puntero saltará a él a[0]=1
, indicando a
el estado en el que se encuentra en este momento, si hay otro en este momento b
, el puntero saltará a b[1]=2
, indicando ab
el estado en el que se encuentra en ese momento, etc.
Después de hacer la matriz de estado de str, es muy conveniente realizar una comparación de cadenas.
txt = "ababcdabasdcdasdfababcdabadc"
test = "ababcdabadc"
KMP(txt,test)
def KMP(txt,test):
status = setStatus(test)
length = len(test)
keySet = set(status.keys())
match = []
j = 0
for i in range(len(txt)):
s = txt[i]
j = status[s][j] if s in keySet else 0
if j==length:
match.append(i-length+1)
return match
def setStatus(test):
length = len(test)
#创建用于存储状态的字典
status = {
s:[0 for _ in range(length)] for s in set(test)}
for ch in status:
for i in range(length):
for j in range(i+1):
if test[i-j:i]+ch == test[0:j+1]:
status[ch][i] = j+1
return status
Optimización de la matriz de coincidencia
En general, aaaaaaa
la matriz de coincidencias para cadenas que no sean la enloquecedora es escasa, lo que significa que hacemos muchas comparaciones inútiles. Por lo tanto, el proceso de solución de su matriz de coincidencia se puede optimizar aproximadamente, al menos se puede eliminar el bucle más externo.
def setStatus(test):
length = len(test)
#创建用于存储状态的字典
status = {
s:[0 for _ in range(length)] for s in set(test)}
for i in range(length):
for j in range(i+1):
if test[i-j:i] == test[0:j]:
ch = test[j]
status[ch][i] = j+1
return status
Ahora solo quedan dos capas de bucles, lo que parece refrescante, pero a las personas que comen melones que no conocen la verdad todavía les importa O ( N 2 ) O(N^2)O ( mujeres )2 )complejidad. Para ser más refrescantes, examinemos las características de la matriz coincidente
b [0, 2, 0, 4, 0, 0, 0, 8, 0, 4, 0]
d [0, 0, 0, 0, 0, 6, 0, 0, 0, 10, 0]
c [0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 11]
a [1, 1, 3, 1, 3, 1, 7, 1, 9, 1, 1]
Primero, la mayoría de los valores distintos de cero se incrementan. Como d
el valor distinto de cero en 6,11
; c
el valor distinto de cero 5,11
en . Y una vez que hay un valor más pequeño, entonces este valor debe haber aparecido antes, por ejemplo b[9]=4
, este 4 ha aparecido antes.
Si se toma 索引降低,则索引必然重复
como un principio, entonces obviamente 0
y 1
también puede incluirse en este principio, b,c,d
el 0 del medio ha aparecido en primer lugar; y a
no hay un 0 en el medio, porque a[0]=1
su valor mínimo solo puede ser 1.
O a[i]
bien i+1
, o un valor que se ha producido alguna vez.
En este caso, para una N
cadena de longitud str
, puedo str
extraer el M
carácter anterior para hacer coincidir hacia atrás. Por ejemplo "ababcdabadc"
, haga coincidir primero para a
obtener un conjunto de posiciones que coincidan con éxito; luego haga coincidir estas posiciones ab
, y así sucesivamente, hasta que la posición coincidente sea la posición 0.