bzoj3172 AC自動機 fail樹

2021-07-29 03:56:02 字數 1716 閱讀 4033

bzoj3172: [tjoi2013]單詞

time limit: 10 sec memory limit: 512 mb

某人讀**,一篇**是由許多單詞組成。但他發現乙個單詞會在**中出現很多次,現在想知道每個單詞分別在**中出現多少次。

input

第乙個乙個整數n,表示有多少個單詞,接下來n行每行乙個單詞。每個單詞由小寫字母組成,n<=200,單詞長度不超過10^6

output

輸出n個整數,第i行的數字表示第i個單詞在文章中出現了多少次。

sample input

3 a

aa aaa

sample output

6 3

1再是一道初識fail樹的經典題,所謂fail樹就是在構建完fail指標後,把所有的fail指標反向,變成一棵樹,每個串終點所在fail樹的子樹中cnt的總和就是這個字串的出現次數,於是每個點記錄所在fail樹的子樹中cnt的數量,可以線性時間內出解

這一大段話有點難理解(如果你是初學者的話),實際上在實現起來不用真正的建一棵fail樹,只要在makefail完成後

cnt[fail[heap[i]]]+=cnt[heap[i]];

heap記錄的是bfs的序列

這裡寫**片

#include///fail樹

#include

#include

#include

using

namespace

std;

int n;

char w[1000010];

int ch[1000010][30],tot=0,fail[1000010];

int cnt[1000010];

int word[256],tt=0,heap[1000010];

void build(int bh)

word[bh]=nw;

return;

}//這道題中字典和需要匹配的字元是一樣的,所以不用單獨再把字典中的東西跑一遍ac自動機

//在建trie時就把經過的點cnt++,相當於匹配了一遍

void make() //make fail

// fail[ch[r][i]]=ch[fail[r]][i];

if (!ch[r][i]) continue; //

int f=fail[r]; //

while(f&&!ch[f][i]) f=fail[f]; //

fail[ch[r][i]]=ch[f][i]; //

q.push(ch[r][i]);

}

}for (i=tt;i;i--)

cnt[fail[heap[i]]]+=cnt[heap[i]];

return; //在進行這一步操作時一定要按照bfs的順序!!!

}int main()

make();

for (int i=1;i<=n;i++)

printf("%d\n",cnt[word[i]]);

return

0;}

交到bzoj上發現我的用時要比其他人多很多,自行**後發現是memset的guo

上面的那一行是使用memeset後的用時

下面這一行是去掉memset後的用時

可見,memset要慎用

BZOJ3172 AC自動機 fail樹

description 某人讀 一篇 是由許多單詞組成。但他發現乙個單詞會在 中出現很多次,現在想知道每個單詞分別在 中出現多少次。input 第乙個乙個整數n,表示有多少個單詞,接下來n行每行乙個單詞。每個單詞由小寫字母組成,n 200,單詞長度不超過10 6 output 輸出n個整數,第i行的...

BZOJ 3172 單詞 (AC自動機)

這道題是個裸的ac自動機,但是我還是調了很久qaq。首先如果我們直接用每個單詞來匹配的,時間不是很理想。這道題要用到ac自動機的衍生物 fail樹 我也是做這道題才知道有這個東西 fail樹有這麼乙個結論 乙個字串出現的次數等於以它為根節點的fail樹的子樹中所有節點的cnt的和。根據這個結論,我們...

bzoj3172 單詞 AC自動機

感覺以前寫過。bzoj上不去我也不知道 跑一遍ac自動機,每乙個節點儲存一下屬於多少字串,為它的權值。然後乙個節點表示的字串在整個字典中出現的次數相當於其在fail樹中的子樹的權值的和。ac自動機不要寫掛就好了。ac 如下 include include include define n 11000...