ac自动机
ac自动机是由树保存。原理有点类似于KMP。失配的时候,我们找到最长后缀进行继续匹配。而且这个是有一颗树保存。
在有些题目时候,故意的KMP过不了,因为如果给你的串够多的话,KMP就是很不好使用
如图:如果我们能够构建一个树(红色代表着如果这个点失配了,会进入指向的地方)这样子会大大减少不必要的比较
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
struct node
{
int next[26] = { 0 };//下一个字母
int fail = -1;//失配指针指向
int cnt = 0;//计算
};
vector<struct node> tree;
int index = 0;//下标
void insert(string s)
{
int root = 0;
int length = s.size();
for (int i = 0; i < length; i++)
{
int word = s[i] - 'a';//这是哪个单词
if (!tree[root].next[word])tree[root].next[word] = ++index;//如果没有分配一个
root = tree[root].next[word];//改变结点
}
tree[root].cnt++;//最后标记一下
}
void fail()
{
queue<int> q;
for(int i = 0;i<26;i++)//先把root下面的结点刷新一下改为指向根节点
if (tree[0].next[i])
{
int son = tree[0].next[i];
tree[son].fail = 0;
q.push(son);//加进来
}
while (!q.empty())
{
int father = q.front();
q.pop();
for (size_t i = 0; i < 26; i++)
{
if (tree[father].next[i])//看看哪个有字母
{
int now = tree[father].next[i];//now保存这个父节点的儿子下标
int fatherfail = tree[father].fail;//找到父亲结点失配指针
while (~fatherfail && tree[fatherfail].next[i])//如果没有到-1并且下面没有点
fatherfail = tree[fatherfail].fail;//继续找前一个失配下标
if (~fatherfail) tree[now].fail = tree[fatherfail].next[i];//如果下面有点就是记录一下
else tree[now].fail = 0;//找到了根节点指向根节点
q.push(now);
}
}
}
}
int query(string s)
{
int ans = 0;
int root = 0;
int length = s.size();
for (int i = 0; i < length; i++)
{
int word = s[i] - 'a';
//如果这个东西发现是没有这个儿子&&没有到达-1那么就是找失配下标
while (!tree[root].next[word] && ~tree[root].fail)root = tree[root].fail;
if (tree[root].next[word])root = tree[root].next[word];//如果有那么记录一下
else continue;
int tmp = root;
while (~tree[tmp].fail)//回溯统计一下答案,毕竟可能匹配到hsi,也有si这个串
{
ans += tree[tmp].cnt;
tmp = tree[tmp].fail;
}
}
return ans;
}