P5357 【模板】AC自動機(二次加強版)(Tire圖)
阿新 • • 發佈:2021-02-11
技術標籤:字串演算法
興沖沖的打出我的 a c ac ac自動機板子,心想這不是和【模板】AC自動機(加強版)一模一樣嗎!!
然後 T L E TLE TLE
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6+10;
int n,id,num[maxn],mp[maxn];
string s[maxn];
char w[maxn];
struct AC_TREE
{
int zi[26],fail,end;
}ac[maxn];
void insert(string a,int index)
{
int n = a.length(), now = 0;
for(int i=0;i<n;i++)
{
if( !ac[now].zi[a[i]-'a'] ) ac[now].zi[a[i]-'a'] = ++id;
now = ac[now].zi[a[i]-'a'];
}
if( ac[now].end==0 )
ac[now].end = index;//記錄是哪個數字出現了
mp[index] = ac[now].end;
}
void Get_Fail()
{
queue<int>q;
for(int i=0;i<=25;i++)
if ( ac[0].zi[i] ) q.push( ac[0].zi[i] ),ac[ac[0].zi[i]].fail = 0;
while( !q.empty() )
{
int u = q.front(); q.pop();
for(int i=0;i<=25;i++)
{
if( ac[u].zi[i] )
{
ac[ac[u].zi[i]].fail = ac[ac[u].fail].zi[i];
q.push( ac[u].zi[i] );
}
else
ac[u].zi[i] = ac[ac[u].fail].zi[i];
}
}
}
void AC_query(char a[] )
{
int n = strlen( a+1 ), now = 0;
for(int i=1;i<=n;i++)
{
now = ac[now].zi[a[i]-'a'];
for(int t=now;t;t=ac[t].fail)
num[ac[t].end]++;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin >> s[i];
insert( s[i],i );
}
Get_Fail();
scanf("%s",w+1);
AC_query(w);
for(int i=1;i<=n;i++) printf("%d\n",num[mp[i]] );
}
其實這麼寫的 a c ac ac自動機複雜度為 O ( n m ) O(nm) O(nm)
注意到我們是通過不斷跳躍 f a i l fail fail指標來減小複雜度,但假如有一個串是
a a a a a a a . . . . a a a a a a a a aaaaaaa....aaaaaaaa aaaaaaa....aaaaaaaa
那麼每個點的 f a i l fail fail指標跳躍後,深度只減小 1 1 1,複雜度上天…
比如這張圖,假如我們匹配到 4 4 4號節點,那麼跳 f a i l fail fail到 7 7 7號節點,再跳到 9 9 9
然後我們下次匹配到節點 7 7 7,又跳 f a i l fail fail到 9 9 9…
實際上,我們可以把次數先存在自己這裡,最後一次性傳給後代
不難發現,如果把 f a i l fail fail指標指向的節點當作父親,自己當作兒子
一遍樹型 d p dp dp就可以解決
所以 a c ac ac自動機只需要加第一個節點的值就好了
void AC_query(char a[] )
{
int n = strlen( a+1 ), now = 0;
for(int i=1;i<=n;i++)
{
now = ac[now].zi[a[i]-'a'];
num[now]++;
}
}
優化後的
a
c
ac
ac自動機,叫做
T
i
r
e
Tire
Tire圖很裝逼的樣子
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e6+10;
int n,id,num[maxn],mp[maxn];
string s[maxn];
char w[maxn];
struct edge{
int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){ d[++cnt] = (edge){v,head[u]},head[u] = cnt; }
struct AC_TREE
{
int zi[26],fail;
}ac[maxn];
void insert(string a,int index)
{
int n = a.length(), now = 0;
for(int i=0;i<n;i++)
{
if( !ac[now].zi[a[i]-'a'] ) ac[now].zi[a[i]-'a'] = ++id;
now = ac[now].zi[a[i]-'a'];
}
mp[index] = now;
}
void Get_Fail()
{
queue<int>q;
for(int i=0;i<=25;i++)
if( ac[0].zi[i] ) q.push( ac[0].zi[i] );
while( !q.empty() )
{
int u = q.front(); q.pop();
for(int i=0;i<=25;i++)
{
if( ac[u].zi[i] )
{
ac[ac[u].zi[i]].fail = ac[ac[u].fail].zi[i];
q.push( ac[u].zi[i] );
}
else ac[u].zi[i] = ac[ac[u].fail].zi[i];
}
}
}
void AC_query(char a[] )
{
int n = strlen( a+1 ), now = 0;
for(int i=1;i<=n;i++)
{
now = ac[now].zi[a[i]-'a'];
num[now]++;
}
}
void dfs(int u)
{
for(int i=head[u];i;i=d[i].nxt )
{ int v = d[i].to; dfs(v); num[u]+=num[v]; }
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
cin >> s[i],insert( s[i],i );
Get_Fail();
scanf("%s",w+1);
AC_query(w);
for(int i=1;i<=id;i++) add(ac[i].fail,i);
dfs(0);
for(int i=1;i<=n;i++) printf("%d\n",num[mp[i]] );
}