A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Algoritmos de Pesquisa em Cadeias de Caracteres

Apresentações semelhantes


Apresentação em tema: "Algoritmos de Pesquisa em Cadeias de Caracteres"— Transcrição da apresentação:

1 Algoritmos de Pesquisa em Cadeias de Caracteres
Referências: Robert Sedgewick, Algorithms in C++, Addison-Wesley 1992, Cap. 19. W. B. Frakes e R. Baeza-Yates, Information Retrieval- Data Structures and Algorithms, Prentice-Hall 1992, Cap. 10.

2 Problema Problema da Pesquisa em Strings
encontrar todas as ocorrências de um padrão de comprimento m num texto de comprimento n texto e padrão são cadeias de caracteres sobre o mesmo alfabeto  com m fixo, esforço computacional é O(n) no pior caso é preciso inspeccionar n-m+1 caracteres no pior caso factor constante na eficiência depende muito do algoritmo Quais as aplicações em ARI - filtragem de resultados usando uma lista de documentos recuperados - destacar os termos da interrogação nos documentos apresentados na saída

3 Algoritmo exaustivo Princípio: tentar identificar o padrão em cada subcadeia de comprimento m do texto Exemplo ...

4 Algoritmo exaustivo public static int pesquisa(String padrao, String texto) { int t, p; int tamPadrao = padrao.length(); int tamTexto = texto.length(); for( t=0, p=0; p < tamPadrao && t < tamTexto; t++, p++) if( texto.charAt(t) != padrao.charAt(p) ) {t = t - p; p = -1; } if (p==tamPadrao) return t - tamPadrao; else return -1;

5 Algoritmo Knuth-Morris-Pratt
Princípio: usar informação obtida em cada “falsa partida” quando a comparação falha, parte dos caracteres estão já examinados e estão no padrão pré-processando a string do padrão, determina-se onde recomeçar quando uma comparação falha não é necessário retroceder no texto; retrocede-se só no padrão Como saber para onde retroceder no padrão: calcular para cada posição do padrão next[j] = max{ i | (pad[k] = pad[j-i+k] para k= 1, ... i-1) e pad[k]  pad[j]}

6 Algoritmo KMP Processamento do padrão resulta em next[j]
é o valor de k<j que identifica o maior prefixo do padrão coincidente com um sufixo do texto já visto detectou-se discordância na posição j do padrão há j caracteres já vistos do texto que coincidem com os do padrão pode recomeçar-se a comparar na posição k se os caracteres anteriores a esta no padrão coincidem com os mais recentemente vistos no texto Como obter next[j] para cada posição j do padrão deslizar uma cópia do padrão sobre si próprio; cópia serve como padrão a procurar no texto (do padrão...) começar com o 1º carácter do padrão sobre o 2º carácter do texto considerar apenas os j caracteres iniciais parar quando todos os caracteres sobrepostos coincidem, ou não há mais next[j] e exemplos supõem os índices a partir de 0. next[0] não se calcula porque o caso do desacordo ao 1º carácter é sempre para avançar no texto e recomeçar no início do padrão. (ver exemplo Sedgewick com padrão de 0’s e 1’s) Os j usados são o índice do carácter onde houve desacordo; j=1 significa que o desacordo foi no 2º carácter. Na versão apresentada o next[j] indica a posição anterior à que vai ser usada para comparar, porque no algoritmo dado se faz sempre o incremento dos ponteiros de texto e de padrão no fim do ciclo. Next[1]= 0 significa que quando houver um desacordo no 2º carácter do padrão se deve avançar no texto e comparar o 2º carácter com o carácter seguinte no texto. Quando o next[j] é -1 vai-se recomeçar na posição 0 do padrão, avançando no texto.

7 Exemplo Algoritmo KMP Exemplo de padrão a b r a c a d a b r a
Para onde retroceder em caso de falha da comparação a b r a c a d a b r a next[j] comparações quem a abra abracadabra abracadabra falha em j=0, avançar e recomeçar em 0 abracadabra falha em j= 0, avançar e recomeçar em 0 abracadabra falha em j= 1, recomeçar em 0 ....

8 Exemplo Algoritmo KMP Exemplo de padrão a b r a c a d a b r a
Para onde retroceder em caso de falha da comparação a b r a c a d a b r a next[j] comparações m ... avançar e recomeçar em 0 a m ... recomeçar em 0 a b m ... recomeçar em 0 a b r m ... avançar e recomeçar em 0 a b r a m ... recomeçar em 1

9 Exemplo Algoritmo KMP

10 next[j] no KMP j next[j] melhor next[j] 1 0 1 0 1 0 0 1 1 1 0

11 Implementação KMP public static int kmp(String padrao, String texto) {
int t, p; int tamPadrao = padrao.length(); int tamTexto = texto.length(); int next[] = new int[tamPadrao]; iniciar(tamPadrao, padrao, next); for( t=0, p=0; p < tamPadrao && t < tamTexto; t++, p++) while( (p >= 0) && (texto.charAt(t) != padrao.charAt(p))) p = next[p]; if (p==tamPadrao) return t - tamPadrao; else return -1; }

12 Posições de retrocesso
public static void iniciar(int tam, String padrao, int next[]) { int i, j; next[0] = -1; for( i = 0, j = -1; i < tam-1; i++; j++; next[i] = j) while( (j>= 0) && (padrao.charAt(i) != padrao.charAt(j)) ) j = next[j]; }

13 Posições de retrocesso
public static void iniciar(int tam, String padrao, int next[]) { int i, j; next[0] = -1; for( i = 0, j = -1; i < tam-1; ) while( (j>= 0) && (padrao.charAt(i) != padrao.charAt(j)) ) j = next[j]; i++; j++; if(padrao.charAt(i) == padrao.charAt(j)) next[i] = next[j]; else next[i] = j)) }

14 Cálculo de posições de retrocesso
i=0 j=-1 next[0] = -1 j >= 0  i=1 j=0 next[1] = 0 j >= 0 p[1] != p[0] j = next[0] = -1 i=2 j=0 next[2] = 0 j >= 0 p[2] != p[0]  i=3 j=1 next[3] = 1 j >= 0 p[3] != p[1]  i=4 j=2 next[4] = 2 j >= 0 p[4] != p[2] j = next[2] = 0 j >= 0 p[4] != p[0] j = next[0] = -1

15 Cálculo de posições de retrocesso
i=5 j=0 next[5] = 0 j >= 0 p[5] != p[0]  i=6 j=1 next[6] = 1 j >= 0 p[6] != p[1] j = next[1] = 0 j >= 0 p[6] != p[0]  i=7 j=1 next[7] = 1 j >= 0 p[7] != p[1] 

16 Exemplo abracadabra i=0 j=-1 next[0] = -1 j >= 0 
j >= 0 p[1] != p[0] j = next[0] = -1 i=2 j=0 next[2] = 0 j >= 0 p[2] != p[0] j = next[0] = -1 i=3 j=0 next[3] = 0 j >= 0 p[3] != p[0]  i=4 j=1 next[4] = 1 j >= 0 p[4] != p[1] j = next[1] = 0 j >= 0 p[4] != p[0] j = next[0] = -1

17 Exemplo abracadabra i=5 j=0 next[5] = 0 j >= 0 p[5] != p[0] 
j >= 0 p[6] != p[1] j = next[1] = 0 j >= 0 p[6] != p[0] j = next[0] = -1 j >= 0  i=7 j=0 next[7] = 0 j >= 0 p[7] != p[0]  i=8 j=1 next[8] = 1 j >= 0 p[8] != p[1]  i=9 j=2 next[9] = 2 j >= 0 p[9] != p[2]  i=10 j=3 next[10]= 3

18 Algoritmo Boyer-Moore
Princípio: fazer comparações da direita para a esquerda do padrão quando a comparação falha no fim do padrão, pode não ser necessário analisar o início pré-processando a string do padrão, determina-se o tamanho do salto possível no texto quando uma comparação falha requer buffer para o texto se este provém de dispositivo auxiliar Heurísticas para avançar no texto: heurística de concordância: quando o padrão desliza tem de continuar a concordar nos caracteres que concordavam trazer um carácter diferente para a posição de discordância no texto heurística do carácter discordante: o padrão desliza até alinhar a posição de discordância no texto com a primeira posição no padrão que concorda com ela

19 Algoritmo Boyer-Moore
Processamento do padrão resulta em skip[j] é o número de caracteres que o padrão pode ser chegado para a direita, quando falha uma comparação detectou-se discordância na posição j do padrão (contagem da direita) há j caracteres já vistos do texto que coincidem com os últimos j do padrão pode avançar-se skip[j] posições no texto e recomeçar a comparação com o fim do padrão saltos são para avançar ponteiro do texto quando se salta, o ponteiro do padrão é reposto no fim deste Algoritmo original combina as 2 heurísticas, usando o maior dos saltos calculados next[j] e exemplos supõem os índices a partir de 0. next[0] não se calcula porque o caso do desacordo ao 1º carácter é sempre para avançar no texto e recomeçar no início do padrão. (ver exemplo Sedgewick com padrão de 0’s e 1’s) Os j usados são o índice do carácter onde houve desacordo; j=1 significa que o desacordo foi no 2º carácter. Na versão apresentada o next[j] indica a posição anterior à que vai ser usada para comparar, porque no algoritmo dado se faz sempre o incremento dos ponteiros de texto e de padrão no fim do ciclo. Next[1]= 0 significa que quando houver um desacordo no 2º carácter do padrão se deve avançar no texto e comparar o 2º carácter com o carácter seguinte no texto. Quando o next[j] é -1 vai-se recomeçar na posição 0 do padrão, avançando no texto.

20 Algoritmo Boyer-Moore
Heurística de concordância deslizar uma cópia do padrão sobre si próprio, da esquerda para a direita; cópia serve como padrão a alinhar na parte conhecida do texto começar com o penúltimo carácter do padrão sobre o último carácter do texto parar quando todos os caracteres sobrepostos nos j finais coincidem, e o carácter na posição j não coincide Heurística do carácter discordante decidir o que fazer com base no carácter do texto que causou a discordância Pré-processamento: decidir, para cada carácter que pode ocorrer no texto, o que fazer se ele causar discordância Tabela não é para posições no padrão mas para caracteres no alfabeto do texto tamanho do salto só depende do carácter analisado, não depende de onde ele fez falhar a comparação; avança-se o ponteiro do texto até alinhar o carácter do texto com o primeiro ponto onde ele aparece no padrão (ocorrência mais à direita) next[j] e exemplos supõem os índices a partir de 0. next[0] não se calcula porque o caso do desacordo ao 1º carácter é sempre para avançar no texto e recomeçar no início do padrão. (ver exemplo Sedgewick com padrão de 0’s e 1’s) Os j usados são o índice do carácter onde houve desacordo; j=1 significa que o desacordo foi no 2º carácter. Na versão apresentada o next[j] indica a posição anterior à que vai ser usada para comparar, porque no algoritmo dado se faz sempre o incremento dos ponteiros de texto e de padrão no fim do ciclo. Next[1]= 0 significa que quando houver um desacordo no 2º carácter do padrão se deve avançar no texto e comparar o 2º carácter com o carácter seguinte no texto. Quando o next[j] é -1 vai-se recomeçar na posição 0 do padrão, avançando no texto.

21 skip[j] com heurística de concordância
j skip[j]

22 Boyer-Moore com heurística de concordância
Exemplo de padrão a b r a c a d a b r a Tamanho do salto em caso de falha da comparação j a b r a c a d a b r a skip[j] comparações (indexação a partir da direita) quem a abra abracadabra abracadabra falha em j=4, avançar 7 abracadabra falha em j= 1, avançar 3 abracadabra falha em j= 0, avançar 1

23 Boyer-Moore com heurística de carácter discordante
Exemplo de padrão a b r a c a d a b r a Tamanho do salto em caso de falha da comparação a b r c d <outras> alfabeto skip[letra] comparações quem a abra abracadabra abracadabra falha em símbolo “a”, avançar 1 abracadabra falha em símbolo não presente, abracadabra avançar 11

24 Implementação BM - carácter discordante
public static int bm(String padrao, String texto) { int t, p, s; int tamPadrao = padrao.length(); int tamAlfa = 256; int tamTexto = texto.length(); int skip[] = new int[tamAlfa]; iniciarBm(tamAlfa, padrao, skip); for( t=tamPadrao-1, p=tamPadrao-1; p > 0; t--, p--) while( texto.charAt(t) != padrao.charAt(p) ) s = skip[ index(texto.charAt(t))]; t += (tamPadrao - p > s) ? tamPadrao - p : s; if ( t >= tamTexto ) return tamTexto; p = tamPadrao-1; } return t;

25 Algoritmo Rabin-Karp Princípio: tratar cada substring de tamanho M do texto como chave de uma tabela de dispersão padrão é encontrado quando a chave da substring coincide com a do padrão como se procura chave específica, não é preciso guardar a tabela, mas apenas calcular as chaves com tabela virtual: grande tamanho reduz probabilidade de falsas escolhas Para ser método eficaz cálculo da chave menos pesado que fazer as comparações função de dispersão: h(k) = k mod q q primo elevado cálculo das chaves: chave da posição i usa a da posição i-1 M caracteres transformam-se em número: empacotados na máquina como palavra, e esta interpretada como número

26 Algoritmo Rabin-Karp M caracteres a[i] ... a[i+M-1] vistos como número inteiro o mesmo que escrever os caracteres como inteiros em base d d: número de caracteres número que representa os M caracteres x = a[i] dM-1 + a[i+1] dM a[i+M-1] conhecendo h(x) = x mod q, é simples passar à chave da string 1 posição à frente (x - a[i] dM-1)d + a[i+M] propriedades da aritmética modular para obter o resto da divisão por q da expressão que resulta de uma operação aritmética, pode operar-se com os valores intermédios em módulo q implementação do algoritmo: calcular chave para o padrão e primeiros M caracteres calcular chaves para as posições sucessivas e comparar com a do padrão

27 Implementação RK public static int rk(String padrao, String texto) {
int q = ; int d = 32; int tamPadrao = padrao.length(); int tamTexto = texto.length(); int i, dM = 1, h1 = 0, h2 = 0; for( i=1; i < tamPadrao; i++) dM = (d * dM) % q; for( i=0; i < tamPadrao; i++) h1 = (h1 * d + index(padrao.charAt(i))) % q; h2 = (h2 * d + index(texto.charAt(i))) % q; } for( i = 0 ; h1 != h2; i++) h2 = (h2 + d * q - index(texto.charAt(i)) * dM) % q; h2 = (h2 * d + index(texto.charAt(i+ tamPadrao))) % q; if( i > tamTexto - tamPadrao ) return tamTexto; return i;

28 Eficiência de pesquisa em texto
Algoritmo exaustivo pode requerer O(N M) comparações Algoritmo Knuth-Morris-Pratt requer no pior caso N + M comparações usa o texto sequencialmente, sem necessidade de recuar Algoritmo de Boyer-Moore caso médio muito mais favorável: estimado em N/M para alfabeto significativamente maior que padrão Algoritmo de Rabin-Karp estimado em O(N), se não ocorrem colisões de chaves


Carregar ppt "Algoritmos de Pesquisa em Cadeias de Caracteres"

Apresentações semelhantes


Anúncios Google