hihoCoder Trie圖(AC自動機)
阿新 • • 發佈:2018-12-20
原來AC自動機是Trie圖啊。。。 Trie圖與KMP的作用相似,都是求字串匹配,而且感覺求法也是類似,不過KMP是單個模式串求匹配,Trie圖是多個模式串求匹配,為了實現這個功能,Tire圖多了一個字首指標,類似KMP的失配陣列
首先按照Tire樹建立好一個樹,然後按照深度優先求取前面深度的字首指標,這個字首指標是根據最大字尾求得。 c字元後面的節點A的字首指向B’,因為abc的最大字尾和bc相同
查詢時,如果一個節點的子節點沒有後續的串,那麼就跳到他的字首指標,在查詢直至到根節點。
程式碼:
程式碼借鑑
typedef struct Trie{
Trie *fail;
Trie * next[26];
int cnt;
Trie() {
memset(next, 0, sizeof(next));
fail = NULL;
cnt = 0;
}
}TrieNode, *LinkTrie;
LinkTrie root;
int head, tail;
void init() {
root = new Trie();
head = tail = 0;
}
void insert(char *st) {
LinkTrie p = root;
while(*st) {
if(p->next[*st-'a'] == NULL)
p->next[*st-'a'] = new Trie();
p = p->next[*st-'a'];
st ++;
}
p->cnt++;
}
void build() {
root->fail = NULL;
deque<LinkTrie> q;
q.push_back(root);
while(!q.empty()) {
LinkTrie tmp = q.front();
LinkTrie p = NULL;
q.pop_front();
for (int i = 0; i < 26; i ++) {
if(tmp->next[i] != NULL) {
if(tmp == root) tmp->next[i]->fail = root;
else {
p = tmp->fail;
while(p != NULL) {
if(p->next[i] != NULL) {
tmp->next[i]->fail = p->next[i];
break;
}
p = p->fail;
}
if(p == NULL) tmp->next[i]->fail = root;
}
q.push_back(tmp->next[i]);
}
}
}
}
int search(char *st) {
int cnt = 0, t;
LinkTrie p = root;
while(*st) {
t = *st - 'a';
while(p->next[t] == NULL && p != root)
p = p->fail;
p = p->next[t];
if(p == NULL) p = root;
LinkTrie tmp = p;
while(tmp != root && tmp->cnt != -1) {//一個模式串只被計算一次
cnt += tmp->cnt;
tmp->cnt = -1;
tmp = tmp->fail;
}
st ++;
}
return cnt;
}
AC程式碼:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
typedef struct Trie{
Trie *fail;
Trie *next[26];
int cnt;
Trie() {
memset(next, 0, sizeof(next));
fail = NULL;
cnt = 0;
}
}TrieNode, *LinkTrie;
LinkTrie root;
int head, tail;
void init() {
root = new Trie();
head = tail = 0;
}
void insert(char *st) {
LinkTrie p = root;
while(*st) {
if(p->next[*st-'a'] == NULL)
p->next[*st-'a'] = new Trie();
p = p->next[*st-'a'];
st ++;
}
p->cnt++;
}
void build() {
root->fail = NULL;
deque<LinkTrie> q;
q.push_back(root);
while(!q.empty()) {
LinkTrie tmp = q.front();
LinkTrie p = NULL;
q.pop_front();
for (int i = 0; i < 26; i ++) {
if(tmp->next[i] != NULL) {
if(tmp == root) tmp->next[i]->fail = root;
else {
p = tmp->fail;
while(p != NULL) {
if(p->next[i] != NULL) {
tmp->next[i]->fail = p->next[i];
break;
}
p = p->fail;
}
if(p == NULL) tmp->next[i]->fail = root;
}
q.push_back(tmp->next[i]);
}
}
}
}
int search(char *st) {
int cnt = 0, t;
LinkTrie p = root;
while(*st) {
t = *st - 'a';
while(p->next[t] == NULL && p != root)
p = p->fail;
p = p->next[t];
if(p == NULL) p = root;
LinkTrie tmp = p;
while(tmp != root && tmp->cnt != -1) {//一個模式串只被計算一次
cnt += tmp->cnt;
tmp->cnt = -1;
tmp = tmp->fail;
}
st ++;
}
return cnt;
}
int main(int argc, char const *argv[]) {
init();
char s[1000005];
int n;
scanf("%d", &n);
while(n --) {
scanf("%s", s);
insert(s);
}
build();
scanf("%s", s);
printf("%s\n", search(s) ? "YES" : "NO");
return 0;
}