日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

AC自动机(写的很乱,仅记录留作自己复习)

發(fā)布時(shí)間:2023/12/19 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AC自动机(写的很乱,仅记录留作自己复习) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

AC自動(dòng)機(jī)

眾所周知,AC自(bu)動(dòng)機(jī)是可以自動(dòng)wa的一種算法(不是

首先,AC自動(dòng)機(jī)解決什么問(wèn)題?

------多字符串匹配問(wèn)題

算法原理是什么?

其基本思想,是字典樹+KMP(僅為思想),字典樹建樹,隨后根據(jù)KMP的思路進(jìn)行點(diǎn)的匹配與跳轉(zhuǎn)(感覺(jué)也有點(diǎn)像并查集)。

【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】、點(diǎn)的匹配與跳轉(zhuǎn)

其原理,是尋找后綴與前綴相同的點(diǎn),進(jìn)行跳轉(zhuǎn)連接。

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數(shù)組記錄字符間相連的線段,首先先將所有與根節(jié)點(diǎn)相連的字符串加入隊(duì)列。

ne數(shù)組則代表點(diǎn)跳躍的地方,當(dāng)p為0時(shí),代表當(dāng)前節(jié)點(diǎn)無(wú)后續(xù)節(jié)點(diǎn),將其指向ne數(shù)組指定位置的后續(xù)節(jié)點(diǎn),由此在匹配時(shí),可以迅速找到某穿字符相匹配的后續(xù)字符串,不需跳回之前的節(jié)點(diǎn);否則,更新節(jié)點(diǎn)p的指向,指向ne數(shù)組指定位置的后續(xù)節(jié)點(diǎn),并加入隊(duì)列。

因?yàn)楫?dāng)前字符串后綴的后綴一定包含在指向字符串前綴的后綴,因此不需要找到所有的后綴,只需連接最長(zhǎng)的后綴即可,不須多次跳轉(zhuǎn),相當(dāng)于路徑壓縮了。

【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;//依據(jù)題意判斷是否歸零p = ne[p];}}return res; }

res:記錄當(dāng)前字符串匹配成功的數(shù)量

j:當(dāng)前跑到的節(jié)點(diǎn)

p:從當(dāng)前節(jié)點(diǎn)依次找到所有滿足的后綴。

每次更新j的時(shí)候,因前面有

if (!p)st[k][i] = st[ne[k]][i];

因此,它能夠跳轉(zhuǎn)到最長(zhǎng)后綴組成的前綴字符串的后續(xù)節(jié)點(diǎn)上,若不匹配則接著跳,直到匹配成功或回歸0點(diǎn)。

當(dāng)匹配成功時(shí),會(huì)依次找到所有滿足其后綴的地方(找后綴的后綴),然后計(jì)算字符串匹配數(shù)目。

例題

ACwing 1282. 搜索關(guān)鍵詞

給定 n 個(gè)長(zhǎng)度不超過(guò) 5050 的由小寫英文字母組成的單詞,以及一篇長(zhǎng)為 m 的文章。

請(qǐng)問(wèn),有多少個(gè)單詞在文章中出現(xiàn)了。

輸入格式

第一行包含整數(shù) T,表示共有 T 組測(cè)試數(shù)據(jù)。

對(duì)于每組數(shù)據(jù),第一行一個(gè)整數(shù) n,接下去 n 行表示 n 個(gè)單詞,最后一行輸入一個(gè)字符串,表示文章。

輸出格式

對(duì)于每組數(shù)據(jù),輸出一個(gè)占一行的整數(shù),表示有多少個(gè)單詞在文章中出現(xiàn)。

數(shù)據(jù)范圍

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; }

總結(jié)

以上是生活随笔為你收集整理的AC自动机(写的很乱,仅记录留作自己复习)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。