2018.12.22 bzoj3473: 字符串(后缀自动机+启发式合并)

传送门

调代码调的我怀疑人生。

启发式合并用迭代写怎么都跑不过(雾

换成了dfsdfsdfs版本的终于过了233.

题意简述:求给出nnn个字串,对于每个给定的字串求出其有多少个字串在至少kkk个剩下字串中出现过。


显然先对所有字串建一个samsamsam出来,然后对于每个状态用一个setsetset维护在哪些字串里面出现过(这个显然需要在建完parentparentparent树之后启发式合并一波),最后拿每个串在上面匹配,如果当前的状态不满足题意就沿着树边向上跳即可。

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int M=2e5+5;
int k,T;
string s[M];
typedef long long ll;
struct SAM{
	int last,tot,len[M],son[M][26],link[M],cnt[M],rk[M],val[M];
	set<int>S[M];
	vector<int>e[M];
	SAM(){last=tot=1,len[0]=-1,fill(son[0],son[0]+26,1);}
	inline void expand(int x,int id){
		int p=last,np=++tot;
		S[last=np].insert(id),len[np]=len[p]+1;
		while(p&&!son[p][x])son[p][x]=np,p=link[p];
		if(!p){link[np]=1;return;}
		int q=son[p][x],nq;
		if(len[q]==len[p]+1){link[np]=q;return;}
		len[nq=++tot]=len[p]+1,memcpy(son[nq],son[q],sizeof(son[q])),link[nq]=link[q];
		while(p&&son[p][x]==q)son[p][x]=nq,p=link[p];
		link[q]=link[np]=nq;
	}
	inline void merge(int u,int v){
		if(S[u].empty()||S[v].empty())return;
		for(set<int>::iterator it=S[v].begin();it!=S[v].end();++it)S[u].insert(*it);
	}
	inline void dfs(int p){
		for(ri i=0,v;i<e[p].size();++i){
			dfs(v=e[p][i]);
			if(S[p].size()<S[v].size())swap(S[p],S[v]);
			for(set<int>::iterator it=S[v].begin();it!=S[v].end();++it)S[p].insert(*it);
		}
		val[p]=S[p].size();
	}
	inline void solve(){
		for(ri i=1;i<=tot;++i)if(link[i])e[link[i]].push_back(i);
		dfs(1);
	}
	inline ll query(int id){
		int p=1,up=s[id].size(),nowlen=0;
		ll ret=0;
		for(ri i=0;i<up;++i){
			p=son[p][s[id][i]-'a'];
			while(val[p]<k)p=link[p];
			ret+=len[p];
		}
		return ret;
	}
}sam;
int main(){
	scanf("%d%d",&T,&k);
	for(ri i=1,n;i<=T;++i){
		cin>>s[i];
		n=s[i].size();
		for(ri j=0;j<n;++j)sam.expand(s[i][j]-'a',i);
		sam.last=1;
	}
	if(k>T){for(ri i=1;i<=T;++i)cout<<"0 ";return 0;}
	sam.solve();
	for(ri i=1;i<=T;++i)cout<<sam.query(i)<<' ';
	return 0;
}
上一篇:题目:写出一条SQL语句,查询工资高于10000,且与他所在部门的经理年龄相同的职工姓名。


下一篇:POJ3415 Common Substrings —— 后缀数组 + 单调栈 公共子串个数