algoritmo de knuth-morris-pratt - kmp
TRANSCRIPT
Algoritmo KMP(Knuth-Morris-Pratt)
Curso de Maratona de ProgramaçãoICT-Unifesp - SJC
2
KMP• Para que serve?• Saber se uma string é substring de outra.• String searching algorithm.
• É legal?• Sim, você escreve pouco (código pequeno) e é eficiente!
• Vídeo-aula utilizando esses slides:• https://youtu.be/VxcrVqoi_k8
3
KMP• Ideia do algoritmo:• Procura uma palavra (padrão) P dentro de um texto T.• Quando aparece uma diferença (mismatch), a palavra tem em si a informação
necessária para determinar onde começar a próxima comparação, ou seja, um mistmach consiste em caracteres que já conhecemos porque eles estão na palavra/padrão (isso evita retroceder pelos caracteres já conhecidos).• Pense: considerando um algoritmo de força bruta, quando ocorre uma
diferença entre T[i] e P[j], não seria possível fazer um deslocamento maior de P para a direita evitando comparações redundantes?
4
KMP• Ideia do algoritmo:
A B A A B X
A B A A B A
i
j
T
P
o maior prefixo que é sufixo é o AB
5
KMP• T[i] != P[j]
A B A A B X
A B A A B A
i
j
T
P
pode-se recomeçar a partir daquiessas comparações não precisam ser refeitas
6
KMP• Exemplo:• texto = "eu gosto de c++"• palavra: "gosto"• A palavra "gosto" foi encontrada na posição 3 (indexando a partir do 0).
7
KMP• Aplicação:• Quando você utiliza um editor de sua preferência e quer buscar uma string
em um arquivo, esses tipos de algoritmos são utilizados para exibir os resultados da busca.
8
KMP• Complexidade:• A complexidade no pior caso é O(n) onde “n” é o tamanho do texto em que a
palavra será buscada.• Um algoritmo ruim teria complexidade O(n * m) onde “n” seria o tamanho do
texto e “m” o tamanho da palavra.
• Duas etapas:• prefix function• string matching
9
KMP• Prefix function:• KMP faz um pré-processamento da palavra (padrão) e constrói um array
auxiliar (vou chamá-lo de “aux”) de tamanho “m” (mesmo tamanho da palavra).• O pré-processamento analisa todos os prefixos do padrão procurando pelo
maior sufixo destes prefixos que também seja prefixo. • O pré-processamento evita que um caractere seja reexaminado!• Exemplo: ABAB
• O maior sufixo que também é prefixo é o “AB” que termina na posição 1 (indexando a partir do 0) e também ocorre na posição 2.
10
KMP• Prefix function:• Ao final, teremos um array que conterá cada posição que gera um prefixo
para cada posição do padrão.• O pré-processamento é feito na palavra (padrão) para determinar se seus
prefixos aparecem como subsequências deles mesmos.• O array que iremos construir nessa fase chamaremos de “aux” de forma que:
• O tamanho do maior prefixo de aux[0..k] que é sufixo de aux[1..k].• Ou seja, é o tamanho do maior “começo” de aux[k] que também aparece no seu “fim”
sem condiderar ele mesmo.
11
KMP
Prefix function
12
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j i
0 1 2 3 4 5 6 7
0
A == B ? Não, coloca 0 na posição marcada por “i” e incrementa “i”
13
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j i
0 1 2 3 4 5 6 7
0 0
A == C ? Nãão, coloca 0 na posição marcada por “i” e incrementa “i”
14
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j i
0 1 2 3 4 5 6 7
0 0 0
A == D ? Nããão, coloca 0 na posição marcada por “i” e incrementa “i”
15
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j i
0 1 2 3 4 5 6 7
0 0 0 0
A == A ? Sim, incrementa “j”, guarda o valor de “j” na posição marcada por “i” e incrementa o “i”
16
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j i
0 1 2 3 4 5 6 7
0 0 0 0 1
B == B ? Simm, incrementa “j”, guarda o valor de “j” na posição marcada por “i” e incrementa o “i”
17
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j i
0 1 2 3 4 5 6 7
0 0 0 0 1 2
C == C ? Simmm, incrementa “j”, guarda o valor de “j” na posição marcada por “i” e incrementa o “i”
18
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j i
0 1 2 3 4 5 6 7
0 0 0 0 1 2 3
D == A ? Não, mas o j é diferente de 0, então podemos fazer: j = aux[j – 1],ou seja, j = 0, não incrementa o “i” e nem o “j’
19
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j i
0 1 2 3 4 5 6 7
0 0 0 0 1 2 3
Voltamos tudo de novo :(, calma, está terminando...A == A ? Sim, incrementa “j”, guarda o valor de “j” na posição marcada por “i” e incrementa o “i”
20
KMP• Considere a palavra: ABCDABCA
A B C D A B C A
j
0 1 2 3 4 5 6 7
0 0 0 0 1 2 3 1
i = 8, ou seja, não é menor do que o tamanhoda palavra ABCDABCA, portanto, chegamos ao fim da execução.
21
KMP• Algoritmo da fase de pré-processamento (prefix function):
substr = palavra = padrão
22
KMP
String matching
23
KMP• Vamos tentar identificar a palavra “CD”• Primeiro fazemos o pré-processamento construindo o vetor “aux”
para o padrão que se quer encontrar.
idx_substr -> índice da substring/palavra/padrãoidx_str -> índice da string/texto
idx_substr = idx_str = 0
C D
0 1
0 0
24
KMP• Vamos tentar identificar a palavra “CD”
C D
0 1
0 0
Critério de parada: idx_str < tam_str
25
KMP• Vamos tentar identificar a palavra “CD”
A B C D A B C A
Lembrando que idx_substr = 0 e idx_str = 0
substr[idx_substr] == str[idx_str] ? C == A ? Não (mismatch)
26
KMP• Vamos tentar identificar a palavra “CD”
A B C D A B C A
Lembrando que idx_substr = 0 e idx_str = 0
idx_substr == tam_substr ?0 == 2 ?
Não, ou seja, a palavra ainda não foi encontrada no texto.
27
KMP• Vamos tentar identificar a palavra “CD”
A B C D A B C A
Lembrando que idx_substr = 0 e idx_str = 0
// mismatches após idx_substr matches
idx_str < tam_str E substr[idx_substr] != str[idx_str] ?Ou seja, 0 < 8 E C != A ? Sim, então verificamos:
idx_substr != 0 ? Não, então idx_str = idx_str + 1 = 1
28
KMP• Vamos tentar identificar a palavra “CD”
A B C D A B C A
idx_substr = 0 e idx_str = 1
substr[idx_substr] == str[idx_str] ? C == B ? Não.idx_substr == tam_substr? 0 == 2 ? Não.
idx_str < tam_str E substr[idx_substr] != str[idx_str] ?1 < 8 E C != B ? Sim.
idx_substr != 0 ? Não, então idx_str = idx_str + 1 = 2
29
KMP• Vamos tentar identificar a palavra “CD”
A B C D A B C A
idx_substr = 0 e idx_str = 2
substr[idx_substr] == str[idx_str], C == C ?Sim, então incrementa idx_substr e idx_str em 1
Agora idx_substr = 1 e idx_str = 3
idx_substr == tam_substr ? 1 == 2 ? Não, ainda não encontramos o padrão.idx_str < tam_str E substr[idx_substr] != str[idx_str] ? Não, porque D == D!
30
KMP• Vamos tentar identificar a palavra “CD”
A B C D A B C A
idx_substr = 1 e idx_str = 3substr[idx_substr] == str[idx_str], D == D ?
Sim, então incrementa idx_substr e idx_str em 1
Agora idx_substr = 2 e idx_str = 4idx_substr == tam_substr ? 2 == 2 ?
Sim, então imprime que o padrão foi encontrado.
31
KMP• Vamos tentar identificar a palavra “CD”
A B C D A B C A
idx_substr = 2 e idx_str = 4
Após a impressão do padrão encontrado, é feito:idx_substr = aux[idx_substr – 1]
Aqui utilizamos o array que construímos no pré-processamento.idx_substr = aux[2 – 1] = aux[1] -> idx_substr = 0
Aqui não fez muita diferença, pois nosso padrão é apenas “CD”, o importante é que nessa parte tenta evitar comparações descenessárias fazendo uso do array “aux”.
C D
0 1
0 0
32
KMP• Vamos tentar identificar a palavra “CD”
A B C D A B C A
E assim sucessivamente, o algoritmo encontra todas as posições do padrão buscado no texto e de forma rápida: O(n)!
33
KMP• Algoritmo da etapa string matching:
34
KMP• Algoritmo:• https://goo.gl/ybXQqN• ou https://gist.github.com/marcoscastro/618bcfc9ec58e3b27661
• Implementação em C++:• https://goo.gl/g0mrcL• ou https://gist.github.com/marcoscastro/55cc08aef269d6d006d2
• Vídeo-aula:• https://youtu.be/VxcrVqoi_k8
35
Considerações sobre strings• Se eu tenho só duas strings e quero achar uma dentro da outra, o que
eu posso usar para otimizar?• Resposta: KMP.
• Se eu tenho várias strings e quero encontrar a ocorrência delas em uma única string, o que eu posso usar para otimizar?• Resposta: Suffix Tree ou Suffix Array.• Otimização: algoritmo de Ukkonen para construir a suffix tree.
• E a Trie (árvore de prefixo)?• Uma alternativa ao map.
36
Contato
www.twitter.com/mcastrosouza