AC自动机(写的很乱,仅记录留作自己复习)
AC自動機
眾所周知,AC自(bu)動機是可以自動wa的一種算法(不是
首先,AC自動機解決什么問題?
------多字符串匹配問題
算法原理是什么?
其基本思想,是字典樹+KMP(僅為思想),字典樹建樹,隨后根據KMP的思路進行點的匹配與跳轉(感覺也有點像并查集)。
【1】、建樹方式
與普通字典樹一模一樣
void insert(string str) {int p = 0;for (int i = 0; i < str.length(); i++) {int t = str[i] - 'a';if (!st[p][t])st[p][t] = ++idx;p = st[p][t];}cnt[p]++; }【2】、點的匹配與跳轉
其原理,是尋找后綴與前綴相同的點,進行跳轉連接。
void built() {queue<int>q;for (int i = 0; i < 26; i++)if (st[0][i])q.emplace(st[0][i]);while (!q.empty()) {int k = q.front();q.pop();for (int i = 0; i < 26; i++) {int p = st[k][i];if (!p)st[k][i] = st[ne[k]][i];else {ne[p] = st[ne[k]][i];q.emplace(p);}}} }這段代碼里,st數組記錄字符間相連的線段,首先先將所有與根節點相連的字符串加入隊列。
ne數組則代表點跳躍的地方,當p為0時,代表當前節點無后續節點,將其指向ne數組指定位置的后續節點,由此在匹配時,可以迅速找到某穿字符相匹配的后續字符串,不需跳回之前的節點;否則,更新節點p的指向,指向ne數組指定位置的后續節點,并加入隊列。
因為當前字符串后綴的后綴一定包含在指向字符串前綴的后綴,因此不需要找到所有的后綴,只需連接最長的后綴即可,不須多次跳轉,相當于路徑壓縮了。
【3】、匹配方式
先看代碼
int findstr(string str) {int res = 0, p = 0, j = 0;for (int i = 0; i < str.length(); i++) {int t = str[i] - 'a';j = st[j][t];p = j;while (p) {res += cnt[p];cnt[p] = 0;//依據題意判斷是否歸零p = ne[p];}}return res; }res:記錄當前字符串匹配成功的數量
j:當前跑到的節點
p:從當前節點依次找到所有滿足的后綴。
每次更新j的時候,因前面有
if (!p)st[k][i] = st[ne[k]][i];因此,它能夠跳轉到最長后綴組成的前綴字符串的后續節點上,若不匹配則接著跳,直到匹配成功或回歸0點。
當匹配成功時,會依次找到所有滿足其后綴的地方(找后綴的后綴),然后計算字符串匹配數目。
例題
ACwing 1282. 搜索關鍵詞
給定 n 個長度不超過 5050 的由小寫英文字母組成的單詞,以及一篇長為 m 的文章。
請問,有多少個單詞在文章中出現了。
輸入格式
第一行包含整數 T,表示共有 T 組測試數據。
對于每組數據,第一行一個整數 n,接下去 n 行表示 n 個單詞,最后一行輸入一個字符串,表示文章。
輸出格式
對于每組數據,輸出一個占一行的整數,表示有多少個單詞在文章中出現。
數據范圍
1≤n≤1e4
1≤m≤1e6
輸入樣例:
1 5 she he say shr her yasherhs輸出樣例:
3代碼
#include<iostream> #include<algorithm> #include<stdio.h> #include<string> #include<string.h> #include<queue>using namespace std;const int N = 10005, S = 51, M = 1000010;int st[N * S][26], cnt[N * S], idx; string str; int ne[N * S]; int m, n;void insert(string str) {int p = 0;for (int i = 0; i < str.length(); i++) {int t = str[i] - 'a';if (!st[p][t])st[p][t] = ++idx;p = st[p][t];}cnt[p]++; }void built() {queue<int>q;for (int i = 0; i < 26; i++)if (st[0][i])q.emplace(st[0][i]);while (!q.empty()) {int k = q.front();q.pop();for (int i = 0; i < 26; i++) {int p = st[k][i];if (!p)st[k][i] = st[ne[k]][i];else {ne[p] = st[ne[k]][i];q.emplace(p);}}} }int findstr(string str) {int res = 0, p = 0, j = 0;for (int i = 0; i < str.length(); i++) {int t = str[i] - 'a';j = st[j][t];p = j;while (p) {res += cnt[p];cnt[p] = 0;p = ne[p];}}return res; }int main() {ios::sync_with_stdio(false);int t;cin >> t;while (t--) {memset(st, 0, sizeof st);memset(ne, 0, sizeof ne);memset(cnt, 0, sizeof cnt);idx = 0;cin >> n;while (n--) {cin >> str;insert(str);}built();cin >> str;cout << findstr(str) << endl;}return 0; }總結
以上是生活随笔為你收集整理的AC自动机(写的很乱,仅记录留作自己复习)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 平衡树-Treap基础内容
- 下一篇: 新一届暑期积分赛题目记录