Алгоритм Ахо-Корасік

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до: навігація, пошук

Алгоритм Ахо - Корасік - алгоритм пошуку підрядка, створений Альфредом Ахо і Маргарет Корасік. Алгоритм реалізує пошук безлічі підрядків із словника в цьому рядку. Час роботи пропорційно O (M + N + K), де N - довжина рядка-зразка, M - сумарна довжина рядків словника, а K - довжина відповіді, тобто сумарна довжина входжень слів із словника в рядок-зразок. Тому сумарний час роботи може бути квадратичним (наприклад, якщо в рядку «ааааааа» ми шукаємо слова «а», «аа», «ааа», ...).

Принцип роботи[ред.ред. код]

Алгоритм будує скінченний автомат, якому потім передає рядок пошуку. Автомат отримує по черзі всі символи рядка й переходить за відповідними ребрами. Якщо автомат досяг кінцевого стану, відповідний рядок словника присутній в рядку пошуку.

Приклад реалізації[ред.ред. код]

Приклад реалізації алгоритму на мові C++

#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <queue>
 
using namespace std;
 
class AhoCorasick
{
public:
    typedef void (*Callback) (const char* substr, int begin, int end);
 
    ~AhoCorasick()
    {
        queue<BorNode*> q;
        for(map<char, BorNode*>::const_iterator iter = root.links.begin();
            iter != root.links.end(); ++iter)
            q.push(iter->second);
        while(!q.empty())
        {
            BorNode* current_node = q.front();
            q.pop();
            for(map<char, BorNode*>::const_iterator iter = current_node->links.begin();
                iter != current_node->links.end(); ++iter)
                q.push(iter->second);
            delete current_node;
        }
    }
 
    // Метод добавляет строку в бор
    void AddString(const char* str)
    {
        BorNode* node = &root;
        for(const char* s = str; *s; ++s)
        {
            map<char, BorNode*>::iterator iter = node->links.find(*s);
            if(iter != node->links.end())
                node = iter->second;
            else
            {
                BorNode* new_node = new BorNode;
                node->links[*s] = new_node;
                node = new_node;
            }
        }
        node->out = words.size();
        words.push_back(str);
    }
 
    // Метод вычисляет функции неудачи
    void Init()
    {
        root.fail = &root;
        queue<BorNode*> q;
        q.push(&root);
        while(!q.empty())
        {
            BorNode* current_node = q.front();
            q.pop();
            for(map<char, BorNode*>::const_iterator iter = current_node->links.begin();
                iter != current_node->links.end(); ++iter)
            {
                BorNode* child = iter->second;
                char symb = iter->first;
                q.push(child);
 
                BorNode* parent_fail = current_node->fail;
                while(true)
                {
                    map<char, BorNode*>::const_iterator it = parent_fail->links.find(symb);
                    if(it != parent_fail->links.end())
                    {
                        child->fail = it->second != child ? it->second : &root;
                        if(child->out < 0)
                            child->out = child->fail->out;
                        break;
                    }
                    if(parent_fail == &root)
                    {
                        child->fail = &root;
                        break;
                    }
                    else
                        parent_fail = parent_fail->fail;
                }
            }
        }
    }
 
    void Search(const char* str, Callback callback)
    {
        BorNode* current_node = &root;
        for(int pos = 1; *str; ++str, ++pos)
        {
            map<char, BorNode*>::const_iterator iter = current_node->links.find(*str);
            while(iter == current_node->links.end())
            {
                current_node = current_node->fail;
                iter = current_node->links.find(*str);
                if(current_node == current_node->fail)
                    break;
            }
            if(iter != current_node->links.end())
            {
                current_node = iter->second;
 
            	if(current_node->out >= 0)
                    callback(words[current_node->out].c_str(), pos - words[current_node->out].length(), pos - 1);
            }
        }
    }
 
private:
    struct BorNode
    {
        BorNode() : fail(NULL), out(-1) {}
 
        map<char, BorNode*> links;
        BorNode* fail;
        int out;
    };
 
    BorNode root;
    vector<string> words;
};
 
void print(const char* str, int start, int end)
{
    cout << "Найдена подстрока " << str << " (начало " << start << ", конец " << end << endl;
}
 
int main()
{
    AhoCorasick ak;
    ak.AddString("test");
    ak.AddString("rok");
    ak.AddString("sto");
    ak.AddString("st");
    ak.Init();
    ak.Search("testovaya_stroka", print);
}

Див. також[ред.ред. код]

Посилання[ред.ред. код]

Візуалізатор алгоритму Ахо - Корасіка
Реалізація алгориитму на C#
Реалізація алгоритму на Java
Реалізація алгоритму на Erlsnd
Детальніший опис алгоритму