1. 程式人生 > 其它 >P5357 【模板】AC自動機(二次加強版)(Tire圖)

P5357 【模板】AC自動機(二次加強版)(Tire圖)

技術標籤:字串演算法

傳送門

興沖沖的打出我的 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]] );
}