1. 程式人生 > >BZOJ3277 串(後綴自動機)

BZOJ3277 串(後綴自動機)

sqrt ons 分隔符 namespace 路徑 後綴 const 分析 stream

  對多串建立SAM的一種方法是加分隔符。於是加完分隔符建出SAM。

  考慮統計出每個節點被多少個串包含。讓每個串各自在SAM上跑,跑到一個節點就標記(顯然一定會完全匹配該節點,因為是對包含其的串建的SAM)並暴跳fail,遇到已經被該串標記過的點就停止。

  這樣暴力的復雜度容易感性證明是O(Lsqrt(L)),因為暴力一個串的過程中,SAM每個點至多被標記一次,每一步跳fail的次數也顯然不會超過該串長度,於是對該串的復雜度是min(L,|S|2),總的最劣復雜度大約就是sqrt(L)個長度為sqrt(L)的串時取得,且常數極小。當然也可以使用樹剖實現,不用分析復雜度就能知道是O(Llog2

L)。

  然後遞推求出每個點被經過時的具體貢獻,也即其到parent樹的根的路徑上所有出現在至少k個串中的點的len-lenfa值的和。再對每個串各自跑一遍累加所經過點的貢獻即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define N 400010
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,m,k,s[N],l[N],r[N],son[N][27],q[N],tot[N],fail[N],len[N],f[N],g[N],cnt=1,last=1;
char qwq[N];
bool flag[N];
vector<int> a;
void ins(int c)
{
    int x=++cnt,p=last;last=x;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[q]==len[p]+1) 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];
        }
    }
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	const char LL[]="%I64d ";
#else
	const char LL[]="%lld ";
#endif
	n=read(),k=read();
	for (int i=1;i<=n;i++)
	{
		scanf("%s",qwq+1);
		int _=strlen(qwq+1);
		l[i]=m+1,r[i]=m+_;
		for (int j=1;j<=_;j++) s[++m]=qwq[j]-‘a‘;
		s[++m]=26;
	}
	for (int i=1;i<=m;i++) ins(s[i]);
	for (int i=1;i<=n;i++)
	{
		int k=1;
		for (int j=l[i];j<=r[i];j++)
		{
			while (!son[k][s[j]]) k=fail[k];
			if (!k) k=1;
			else 
			{
				k=son[k][s[j]];
				for (int x=k;!flag[x];x=fail[x])
				flag[x]=1,f[x]++,a.push_back(x);
			}
		}
		for (int j:a) flag[j]=0;a.clear();
	}
	for (int i=1;i<=cnt;i++) tot[len[i]]++;
	for (int i=1;i<=m;i++) tot[i]+=tot[i-1];
	for (int i=1;i<=cnt;i++) q[tot[len[i]]--]=i;
	for (int i=1;i<=cnt;i++)
	{
		int x=q[i];
		g[x]=g[fail[x]];
		if (f[x]>=k) g[x]+=len[x]-len[fail[x]];
	}
	for (int i=1;i<=n;i++)
	{
		ll ans=0;int k=1;
		for (int j=l[i];j<=r[i];j++)
		{
			while (!son[k][s[j]]) k=fail[k];
			if (!k) k=1;
			else k=son[k][s[j]],ans+=g[k];
		}
		printf(LL,ans);
	}
	return 0;
}

  

BZOJ3277 串(後綴自動機)