Hendrik Jan está correto sobre o algoritmo Knuth-Morris-Pratt (aviso, a wikipedia não o explica particularmente bem, um texto sobre algoritmos é provavelmente uma aposta melhor). A função de falha pode ser usada para extrair um DFA que pode executar a correspondência de string. Não é imediatamente óbvio que a tabela de falhas é um DFA, mas com muito pouco trabalho, a tabela de transição para o DFA pode ser construída. E seW é a sequência padrão que você deseja corresponder, o tempo para criar a tabela (e, portanto, construir o DFA) é O(|W|).
A ideia central é que a tabela de falhas T (Vou tentar usar os mesmos nomes da wikipedia, apenas para obter um exemplo pronto) mostra onde você pode estar na sequência se a correspondência atual não der certo, ou seja, se você não vir o próximo personagem que você está esperando, talvez o início da partida que você deseja já tenha sido visto, você está erroneamente mais à frente, então você só quer "voltar atrás" um pouco (note que não há retrocesso real no senso algorítmico usual).
Então, roubando descaradamente o exemplo da wikipedia, digamos que temos a tabela de falhas T:
iW[i]T[i]0A−11B02C03D04A05B16D2
Podemos construir o DFA da seguinte maneira; temos|W|+1estados, com uma espinha dorsal de transições que correspondem ao padrão inteiro. Para corresponder à tabela, chamaremos o estado inicialq−1 e o estado final q|W| (então no exemplo q6) O backbone é então as transiçõesδ(qi−1,W[i])=qi, então, no exemplo, vamos do estado inicial q−1 declarar q0 em um A e de q2 para q3 com um D.
Agora, o truque é adicionar as transições "para trás" corretas, para que não retrocedamos ao longo da espinha dorsal quando entendemos algo errado. É aqui que nós a função de falha. Se estamos no estadoqi−1 e não vemos W[i] como o próximo símbolo, podemos alternativamente fazer a transição para qT[i] se vemos W[T[i]]. Do exemplo, se estamos no estadoq5e não vemos um D, mas vemos uma C, podemos ir para q2. Todos os outros símbolos retornam ao estado inicial.
Reiterando; existem três tipos de transições, a transição de backbone, se tudo estiver indo bem, a transição dada pela tabela de falhas para que possamos recuperar um pouco e|Σ|−2 outras transições que nos levam de volta ao estado inicial.
Os estados inicial e final são um pouco especiais, é claro, o estado inicial não pode voltar mais, então a transição de recuperação de falha aparece com as outras transições e, quando atingimos o estado final, não importa o que mais vemos , então também é um estado de afundamento.
Há uma ruga final, o caso em que o símbolo de falha é o mesmo que o esperado (por exemplo, W[1] e W[5]) nesse caso, podemos adiar a decisão até que sejam diferentes ou criar um NFA.
O exemplo resulta em um DFA parecido com (exceto erros):
As transições tracejadas representam as transições "tudo o que resta".
Deve ser fácil ver que, dada a tabela, podemos construir o DFA em O(|W|)tempo (de uma só vez). A tabela também pode ser construída emO(|W|)time - o código fonte na wikipedia é suficiente. Se formos espertos, é claro que podemos pular a etapa intermediária da tabela e usar o algoritmo de criação de tabelas para criar o DFA imediatamente. Esse tempo de execução também é o melhor que podemos esperar, pois um DFA que corresponde apenas a essa sequência precisa ter pelo menos|W| transições (e |W|+1 estados), então precisamos ler a string inteira W, o que leva Ω(|W|) passos.