簡單版AC自動機

来源:https://www.cnblogs.com/kylinbalck/archive/2019/02/23/10424306.html
-Advertisement-
Play Games

簡單版$AC$自動機 學之前聽別人說起一直以為很難,今天學了簡單版的$AC$自動機,感覺海星,只要理解了$KMP$一切都好說。 前置知識: "$KMP$" (有鏈接) 前置知識:$Trie$樹 字典樹($Trie$樹)比較簡單,就是把許多個單詞通過樹連接起來。每個點記錄一下兒子個數以及是否是單詞結尾 ...


簡單版\(AC\)自動機

學之前聽別人說起一直以為很難,今天學了簡單版的\(AC\)自動機,感覺海星,只要理解了\(KMP\)一切都好說。

前置知識:\(KMP\)(有鏈接)

前置知識:\(Trie\)

字典樹(\(Trie\)樹)比較簡單,就是把許多個單詞通過樹連接起來。每個點記錄一下兒子個數以及是否是單詞結尾即可。每次加入一個單詞時,從第一個字母開始搜索,如果當前字母存在,就從該字母的兒子里找下一個字母,否則就新建一個節點,直到把這個單詞全部加入進去,然後在最後的字元上標記一下表示以這個字母結尾的單詞多了一個。

那麼\(AC\)自動機實際上就是將兩者合併了起來,在字典樹上進行\(KMP\)

先說一下\(AC\)自動機是乾什麼的。一個常見的例子就是給出n個單詞,再給出一段包含m個字元的文章,讓你找出有多少個單詞在文章里出現過。要搞懂AC自動機,先得有模式樹(字典樹)\(Trie\)\(KMP\)模式匹配演算法的基礎知識。\(KMP\)演算法是單模式串的字元匹配演算法,\(AC\)自動機是多模式串的字元匹配演算法。

說白了就是給你一堆字元串,然後再給你一個字元串,問最後這個字元串中出現了多少個前面給出的字元串。

首先我們要有一個字典樹。對於給出的那一堆字元串,我們要一個一個加到樹里。代碼如下:

struct AC{//字典樹
    int end,vis[26],fail;//vis表示兒子的編號
}AC[1000006];
int cnt;
void Build(string s){//要加入的單詞
    int l=s.length(),now=0;//now是當前節點
    for(int i=0;i<l;++i){
        if(AC[now].vis[s[i]-'a']==0) AC[now].vis[s[i]-'a']=++cnt;
        //如果這個字母沒有,就新建一個
        now=AC[now].vis[s[i]-'a'];//如果有或者已經建好,就往下跳
    }
    AC[now].end++;//在最後一個字母處標記有幾個單詞以它結尾
}

有了字典樹,考慮怎樣在樹上進行\(KMP\)

\(KMP\)裡面的\(next\)指針在這裡改成\(fail\),其實都一樣。

每個節點\(t\)\(fail\)指針,其所指向的節點和\(t\)節點的字元是一樣的。因為如果\(t\)匹配成功,而\(t\)的兒子匹配失敗,那麼需要從\(t\)\(fail\)指針的兒子節點開始匹配。

\(fail\)指針用\(BFS\)來求。

首先,根節點的\(fail\)指針顯然指向他自己,即\(0\)。而他的兒子,也就是深度為一的節點的指針也是指向他的。那麼考慮剩下的節點\(t\)。它的父親節點的\(fail\)指針已經知道,那麼這個指針指向的節點假如是\(u\)的話,如果\(u\)有一個和\(t\)一樣的節點,那麼\(t\)\(fail\)指針就應該指向它,如果沒有,就要從\(father->fail->fail\)里找,直到找到相同的節點或者到根節點。也就是說要順著之前的失配指針走一遍,有點麻煩。

考慮如果當前節點沒有某個字母,那麼我們可以將該節點指向這個字母的指針,指到他的失配指針指向的節點的這個字母上。

if(AC[u].vis[i]==0)
    AC[u].vis[i]=AC[AC[u].fail].vis[i];

這樣就不用沿著失配指針走一遍了。代碼如下:

void Get_fail(){
    queue<int>Q;//隊列,bfs
    for(int i=0;i<26;++i)//處理深度為二的點
        if(AC[0].vis[i]) AC[AC[0].vis[i]].fail=0,Q.push(AC[0].vis[i]);
    while(!Q.empty()){
        int u=Q.front();
        for(int i=0;i<26;++i)
            if(AC[u].vis[i]) AC[AC[u].vis[i]].fail=AC[AC[u].fail].vis[i],Q.push(AC[u].vis[i]);
            //如果有這個點,就直接更新指針並壓入隊列
            else AC[u].vis[i]=AC[AC[u].fail].vis[i];//沒有就按上述方法處理
        Q.pop();
    }
}

最後就是統計了。

對於每個字母,如果他是幾個單詞的結尾,那麼久加上他的以及他的所有失配指針的答案,因為他可以,他的失配指針同樣可以。

代碼:

int AC_query(string s){
    int l=s.length(),now=0,ans=0;
    for(int i=0;i<l;++i){
        now=AC[now].vis[s[i]-'a'];
        for(int t=now;t&&AC[t].end!=-1;t=AC[t].fail)//沿著失配指針跳
            ans+=AC[t].end,AC[t].end=-1;//統計答案,標記-1為了防止重覆統計
    }
    return ans;
}

\(Code\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
struct AC{
    int end,vis[26],fail;
}AC[1000006];
int cnt;
void Build(string s){
    int l=s.length(),now=0;
    for(int i=0;i<l;++i){
        if(AC[now].vis[s[i]-'a']==0) AC[now].vis[s[i]-'a']=++cnt;
        now=AC[now].vis[s[i]-'a'];
    }
    AC[now].end++;
}
void Get_fail(){
    queue<int>Q;
    for(int i=0;i<26;++i)
        if(AC[0].vis[i]) AC[AC[0].vis[i]].fail=0,Q.push(AC[0].vis[i]);
    while(!Q.empty()){
        int u=Q.front();
        for(int i=0;i<26;++i)
            if(AC[u].vis[i]) AC[AC[u].vis[i]].fail=AC[AC[u].fail].vis[i],Q.push(AC[u].vis[i]);
            else AC[u].vis[i]=AC[AC[u].fail].vis[i];
        Q.pop();
    }
}
int AC_query(string s){
    int l=s.length(),now=0,ans=0;
    for(int i=0;i<l;++i){
        now=AC[now].vis[s[i]-'a'];
        for(int t=now;t&&AC[t].end!=-1;t=AC[t].fail)
            ans+=AC[t].end,AC[t].end=-1;
    }
    return ans;
}
int main(){
    int n;
    string s;
    cin>>n;
    for(int i=1;i<=n;++i) cin>>s,Build(s);
    AC[0].fail=0;
    Get_fail();
    cin>>s;
    cout<<AC_query(s);
    return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基礎數據類型 1、什麼是數據類型? 我們人類可以很容易的分清數字與字元的區別,但是電腦並不能呀,電腦雖然很強大,但從某種角度上看又很傻,除非你明確的告訴它,1是數字,“漢”是文字,否則它是分不清1和‘漢’的區別的,因此,在每個編程語言里都會有一個叫數據類型的東東,其實就是對常用的各種數據類型進行 ...
  • 為了防止服務消費鏈(多級服務之間的調用)上因某個服務出現故障,而導致級聯故障,進而造成整個系統不可用(簡稱為:雪崩效應),推出了熔斷、降級的處理方式:Hystrix斷路器(類似生活中電路保險絲)來解決這些潛在問題。 熔斷、降級是兩個概念,網上也有很多相關的說明,我這裡簡單通俗說明一下: 熔斷:當服務 ...
  • 在介紹了通用的序列操作後,我們來學習序列類型中的列表和元組 列表 回顧 我們已經初步學習了列表,在深入之前,讓我們簡單回顧一下以往的知識。 創建列表的方法: 給元素賦值: 刪除元素: 上一節我們還學習了分片、相加、乘法等通用序列操作,這裡就不過多闡述 分片賦值 在上一節中,我們介紹了通用的序列分片操 ...
  • 首先, 引入這節需要的 csv 文件 (已上傳) 輸出: 根據 'city'欄位分組: 輸出: 迴圈輸出分組後的數據: 輸出: 獲取其中的某一組數據: 輸出: 取每個組的最大值: 取每個組的平均值: 獲取每個組的常規信息: 輸出圖表: 以上, 就是關於 Pandas 分組的相關知識, enjoy~~ ...
  • 以下示例演示如何在 MATLAB® 中創建各種二維圖。 線圖 plot 函數用來創建由 x 和 y 值繪製而成的簡單線圖。 x = 0:0.05:5; y = sin(x.^2); figure plot(x,y) 線圖可顯示多組 x 和 y 數據。 y1 = sin(x.^2); y2 = cos ...
  • 表達式中運算數據類型不一致怎麼辦? 參數傳遞:就是調用方法的時候,向方法內傳入數據的動作。 形式參數:在定義方法的時候,寫在小括弧之內的參數。(被動接收數據的) eg:public static int sum(int a,int b)//這裡的a和b,是在定義的時候寫的,所以是形式參數即形參。 實 ...
  • 79、字元串排序。 80、海灘上有一堆桃子,五隻猴子來分。第一隻猴子把這堆桃子平均分為五份,多了一個,這隻猴子把多的一個扔入海中,拿走了一份。第二只猴子把剩下的桃子又平均分成五份,又多了一個,它同樣把多的一個扔入海中,拿走了一份,第三、第四、第五隻猴子都是這樣做的,問海灘上原來最少有多少個桃子? 8 ...
  • 首先說明:以版本為Spring 4.3.0為測試對象; 開啟<mvc:annotation-driven /> 測試場景一:請求中含有date屬性,該類型為日期類型,SpringMvc採用@RequestParam來接受作為方法入參。 代碼很簡單,第一反應是不能將字元串的date屬性賦給d; 先嘗試 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...