算法记录 002: SAM 上的根号暴力

实用算法 002: SAM 上的根号暴力

题目

看到了这个神仙代码:code

他对于每一个每一个SAM上的节点都暴力跳上去。这样猛然一看不是非常对,但是仔细分析一波,这是一个非常优美的根号暴力。

\(proof:\)

设当前考虑字符串\(s_i\),每次向上跳可以补充不漏的跳出每一个子串,也就是最多\(O(|s_i|^2)\)次,但是这是非常不满的,因为也不太好构造SAM上每一个节点只对应一个串。但是总共跳的次数肯定也是\(\leq O(N)\)的(对于每一个串而言)。

则对于i串,它最多会贡献\(\min(n,|s_i|^2)\)。其中\(n=\sum |s_i|\),则时间复杂度为\(O(n\sqrt n)\)

code for cf 204e

/*
{
######################
#       Author       #
#        Gary        #
#        2021        #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<'0'||ch>'9'){
//        ch=getchar();
//    }
//    while(ch>='0'&&ch<='9'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXLEN=1e5+20;
struct state{
	int len,link,minlen;
	unordered_map<char,int> ch;
};
struct SAM{	
	int sz=0,last;
	state st[MAXLEN+MAXLEN];
	bool vis[MAXLEN+MAXLEN];
	int siz[MAXLEN+MAXLEN];//出现次数 
	vector<int> g[MAXLEN+MAXLEN];
	int siz2[MAXLEN+MAXLEN];//状态大小 
	void sam_init(){
		memset(vis,0,sizeof(vis));
		st[0].len=0;
		st[0].link=-1;
		sz=0;
		last=0;
	}
	SAM(){
		sam_init();
	}
	void sam_extend(char c){
		int cur=++sz;
		st[cur].len=st[last].len+1;
		int p=last;
		while(p!=-1&&st[p].ch.find(c)==st[p].ch.end()){
			st[p].ch[c]=cur;
			p=st[p].link;
		} 
		if(p==-1){
			st[cur].link=0;
		}
		else{
			int q=st[p].ch[c];
			if(st[q].len==st[p].len+1){
				st[cur].link=q;
			}
			else{
				int clone;
				clone=++sz;
				st[clone].len=st[p].len+1;
				st[clone].ch=st[q].ch;
				st[clone].link=st[q].link;
				while(p!=-1&&st[p].ch.find(c)!=st[p].ch.end()&&st[p].ch[c]==q){
					st[p].ch[c]=clone;
					p=st[p].link;
				}
				st[cur].link=st[q].link=clone;
			}
		}
		last=cur;
		siz[cur]++;
	}
	void dfs(int now){
		vis[now]=true;
		for(auto it:g[now]){
			if(!vis[it]) dfs(it);
			siz[now]+=siz[it];
		}
	}
	void dfs2(int now){
		vis[now]=1;
		for(auto it:g[now]){
			if(!vis[it]) dfs2(it);
			siz2[now]+=siz2[it];
		}
	}
	void run(){
		rb(i,1,sz) g[st[i].link].PB(i);
		rb(i,0,sz) if(!vis[i]) dfs(i);
		rb(i,0,sz) g[i].clear();
		memset(vis,0,sizeof(vis));
		siz2[0]=1;
		rb(i,0,sz) for(auto ite=st[i].ch.begin();ite!=st[i].ch.end();ite++) g[ite->SEC].PB(i);
		rb(i,0,sz) if(!vis[i]) dfs2(i);
		rb(i,1,sz) st[i].minlen=st[i].len-siz2[i]+1;
	}
	void gen(string s){
		rep(i,s.length()) sam_extend(s[i]);	
	}
}sam;
string s[MAXLEN];
int vis[MAXLEN+MAXLEN];
int cnt[MAXLEN+MAXLEN];
void paint(int now,int col){
	while(now!=-1){
		if(vis[now]==col) break;
		vis[now]=col;
		cnt[now]++;
		now=sam.st[now].link;
	}
}
int n,k;
LL dp[MAXLEN+MAXLEN];
void calc(int now){
	vis[now]=1;
	if(now!=0){
		if(cnt[now]>=k)
		dp[now]=sam.siz2[now];
		int pre=sam.st[now].link;
		if(!vis[pre]) calc(pre);
		dp[now]+=dp[pre];
	}
}
int main(){
	scanf("%d%d",&n,&k);
	rb(i,1,n){
		sam.last=0;
		cin>>s[i];
		sam.gen(s[i]);
	}
	sam.run();
	rb(i,1,n){
		int now=0;
		rep(j,s[i].length()){
			now=sam.st[now].ch[s[i][j]];
			paint(now,i);
		}		
	}
	memset(vis,0,sizeof(vis));
	rb(i,1,sam.sz){
		if(!vis[i]){
			calc(i);
		}
	}
	rb(i,1,n){
		int now=0;
		LL rest=0;
		rep(j,s[i].length()){
			now=sam.st[now].ch[s[i][j]];
			rest+=dp[now];
		}
		printf("%lld ",rest);
	}
	return 0;
}

posted @ 2021-01-05 22:24  WWW~~~  阅读(124)  评论(0编辑  收藏  举报