BZOJ 5137: [Usaco2017 Dec]Standing Out from the Herd(后缀自动机)

传送门

解题思路

  这个似乎和以前做过的一道题很像,只不过这个是求本质不同子串个数。肯定是先把广义\(SAM\)造出来,然后\(dfs\)时把子节点的信息合并到父节点上,看哪个只被一个串覆盖,\(ans+=l_i-l_{fa_i}\)。这里我比较懒(sha),合并信息直接暴力扔了个\(set\)启发式合并上去的,似乎分类讨论一下就行了,平添\(log\)正是在下。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<string>
#include<vector>

using namespace std;
const int N=100005;
typedef long long LL;

int n,a[N<<1],c[N<<1],siz[N<<1];
LL ans[N];
set<int> S[N<<1];
vector<int> v[N<<1];
string s[N];

void add(int bg,int ed){
	v[bg].push_back(ed);
}

void dfs(int x){
	int u;
	for(int i=0;i<v[x].size();i++){
		u=v[x][i]; dfs(u);
		if(x==1) continue;
		for(set<int>::iterator it=S[u].begin();it!=S[u].end();it++)
			S[x].insert(*it);
	}
	siz[x]=S[x].size();
}

struct SAM{
	int fa[N<<1],ch[N<<1][28],l[N<<1],cnt,lst;
	void Insert(int c,int id){
		int p=lst,np=++cnt; l[np]=l[p]+1; lst=cnt;
		for(;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
		if(!p) fa[np]=1;
		else {
			int q=ch[p][c]; 
			if(l[q]==l[p]+1) fa[np]=q;
			else {
				int nq=++cnt; l[nq]=l[p]+1;
				memcpy(ch[nq],ch[q],sizeof(ch[nq]));
				fa[nq]=fa[q]; fa[q]=fa[np]=nq;
				for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
			}
		} 
		S[np].insert(id);
	}
	void solve(){
		for(int i=1;i<=cnt;i++)
			if(fa[i]) add(fa[i],i);
		dfs(1);
	}
}sam;

int main(){
	scanf("%d",&n); int len; sam.cnt=1;
	for(int i=1;i<=n;i++){
		cin>>s[i]; len=s[i].length(); sam.lst=1; 
		for(int j=0;j<len;j++) sam.Insert(s[i][j]-'a'+1,i);
	}
	sam.solve();
	for(int i=1;i<=sam.cnt;i++)
		if(siz[i]==1) ans[*S[i].begin()]+=sam.l[i]-sam.l[sam.fa[i]];
	for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
	return 0;
}	
posted @ 2019-02-28 22:11  Monster_Qi  阅读(149)  评论(0编辑  收藏  举报