BZOJ3926 ZJOI2015諸神眷顧的幻想鄉(廣義後綴自動機)

  對多串創建SAM的一種方法是建trie再對trie建SAM。構造方式分爲在線(也即不建trie而是依次插入每一個串,或在trie上dfs)和離線(也即建好trie再bfs)。其中離線構造與單串的構造方式幾乎沒有區別,將父親所在節點做爲last便可,按bfs序建SAM能夠避免出現要轉移到的狀態已經存在的狀況。而在線構造時則須要考慮這點,若存在除了不用新建節點其他操做也與單串區別不大。c++

  這種作法相比加分隔符建SAM,一方面避免了存在分隔符的尷尬狀態的存在,方便於統計一些東西。(好比本題的本質不一樣子串數量,若是加分隔符就還要考慮一下right集合,不過好像影響也不是特別大?)另外一方面這樣建出的SAM總狀態數是O(|T|)的,轉移函數也即邊數是O(|T||A|)的,雖然在線構造的複雜度是O(G(T)+|T||A|),但更簡單的離線構造能夠作到理論最低複雜度O(|T||A|)(|T|爲trie總節點數,|A|爲字符集大小,G(T)爲trie上全部葉子深度之和,據論文,我固然啥都不知道);而加分隔符建SAM的上述全部複雜度都是O(G(T))。也就是說這種作法在直接給出trie的題中會弔打加分隔符。函數

  對於本題,若是離線構造,拎出每一個葉子做爲根獲得的trie合併起來建SAM便可。在線構造只要以每一個葉子爲根dfs並插進SAM。spa

  在線:blog

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 4000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,a[N],p[N],degree[N],id[N],son[N][10],fail[N],len[N],t,cnt=1;
ll ans;
struct data{int to,nxt;
}edge[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
int ins(int c,int p)
{
    if (son[p][c])
    {
        int q=son[p][c];
        if (len[p]+1==len[q]) return q;
        else
        {
            int y=++cnt;
            len[y]=len[p]+1;
            memcpy(son[y],son[q],sizeof(son[q]));
            fail[y]=fail[q],fail[q]=y;
            while (son[p][c]==q) son[p][c]=y,p=fail[p];
            return y;
        }
    }
    else
    {
        int x=++cnt;len[x]=len[p]+1;
        while (!son[p][c]&&p) son[p][c]=x,p=fail[p];
        if (!p) fail[x]=1;
        else
        {
            int q=son[p][c];
            if (len[p]+1==len[q]) fail[x]=q;
            else
            {
                int y=++cnt;
                len[y]=len[p]+1;
                memcpy(son[y],son[q],sizeof(son[q]));
                fail[y]=fail[q],fail[x]=fail[q]=y;
                while (son[p][c]==q) son[p][c]=y,p=fail[p];
            }
        }
        return x;
    }
}
void dfs(int k,int from)
{
    id[k]=ins(a[k],id[from]);
    for (int i=p[k];i;i=edge[i].nxt)
    if (edge[i].to!=from) dfs(edge[i].to,k);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj3926.in","r",stdin);
    freopen("bzoj3926.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read();read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<n;i++)
    {
        int x=read(),y=read();
        addedge(x,y),addedge(y,x);
        degree[x]++,degree[y]++;
    }
    id[0]=1;
    for (int i=1;i<=n;i++)
    if (degree[i]==1) dfs(i,0);
    for (int i=1;i<=cnt;i++) ans+=len[i]-len[fail[i]];
    cout<<ans;
    return 0;
}

 

  離線:get

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 4000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,c,a[N],p[N],degree[N],id[N],son[N][10],q[N],trie[N][10],fail[N],len[N],t,cnt;
ll ans;
struct data{int to,nxt;
}edge[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
int ins(int c,int p)
{
	int x=++cnt;len[x]=len[p]+1;
	while (!son[p][c]&&p) son[p][c]=x,p=fail[p];
	if (!p) fail[x]=1;
	else
	{
		int q=son[p][c];
		if (len[p]+1==len[q]) fail[x]=q;
		else
		{
			int y=++cnt;
			len[y]=len[p]+1;
			memcpy(son[y],son[q],sizeof(son[q]));
			fail[y]=fail[q],fail[x]=fail[q]=y;
			while (son[p][c]==q) son[p][c]=y,p=fail[p];
		}
	}
	return x;
}
void dfs(int k,int from)
{
	if (!trie[id[from]][a[k]]) trie[id[from]][a[k]]=++cnt;
	id[k]=trie[id[from]][a[k]];
	for (int i=p[k];i;i=edge[i].nxt)
	if (edge[i].to!=from) dfs(edge[i].to,k);
}
void bfs()
{
	int head=0,tail=1;q[1]=0;id[0]=1;cnt=1;
	do
	{
		int x=q[++head];
		for (int i=0;i<c;i++)
		if (trie[x][i])
		{
			q[++tail]=trie[x][i];
			id[trie[x][i]]=ins(i,id[x]);
		}
	}while (head<tail);
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("bzoj3926.in","r",stdin);
	freopen("bzoj3926.out","w",stdout);
	const char LL[]="%I64d\n";
#else
	const char LL[]="%lld\n";
#endif
	n=read(),c=read();
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<n;i++)
	{
		int x=read(),y=read();
		addedge(x,y),addedge(y,x);
		degree[x]++,degree[y]++;
	}
	for (int i=1;i<=n;i++)
	if (degree[i]==1) dfs(i,0);
	bfs();
	for (int i=1;i<=cnt;i++) ans+=len[i]-len[fail[i]];
	cout<<ans;
	return 0;
}

  離線甚至慢了一倍,多是要先建trie的緣由。it

相關文章
相關標籤/搜索