【学习笔记】SAM的应用

command_block-SAM的应用
感谢大佬的指点

暴论:后缀数组什么辣鸡啊,再也不用后缀数组啦!加入SAM神教!


建出后缀自动机,可知一个串的出现次数即为endpos个数也是后缀链接树上的子节点个数。
同一endpos集合的子集中子串长度连续且长度区间为 [maxlen(sam[cur].link)+1,maxlen(cur)]
两者乘起来即为答案,要开longlong

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar(); 
	}
	return f*j;
}
const int N=200010;
char s[N];
int n,cnt,du[N];
struct node{
	int lin,siz,len,to[26],sumn;
}sam[N];
inline int insert(int p,int sumn){
	int cur=++cnt;
	sam[cur].siz=1,sam[cur].len=sam[p].len+1,sam[cur].sumn=sumn;
	while(p&&!sam[p].to[sumn])sam[p].to[sumn]=cur,p=sam[p].lin;
	if(!p)return sam[cur].lin=1,cur;
	int q=sam[p].to[sumn];
	if(sam[q].len==sam[p].len+1)return sam[cur].lin=q,cur;
	int clone=++cnt;
	sam[clone]=sam[q],sam[clone].len=sam[p].len+1,sam[clone].siz=0;
	sam[q].lin=sam[cur].lin=clone;
	while(p&&sam[p].to[sumn]==q)sam[p].to[sumn]=clone,p=sam[p].lin;
	return cur;
}
void work(){
	long long ans=0;
	for(int i=1;i<=cnt;i++)sam[i]=sam[0],du[i]=0;
	scanf("%s",s+1),n=strlen(s+1);
	cnt=1;
	for(int last=1,i=1;i<=n;i++)last=insert(last,s[i]-'a');
	for(int i=1;i<=cnt;i++)du[sam[i].lin]++;
	deque<int>que;
	for(int i=1;i<=cnt;i++)if(!du[i])que.push_back(i);
	while(!que.empty()){
		int u=que.front();que.pop_front();
//		cout<<u<<":"<<sam[u].lin<<"-"<<sam[u].len<<" "<<sam[u].siz<<"-"<<(char)(sam[u].sumn+'a')<<"\n";
		ans+=1ll*(sam[u].len-sam[sam[u].lin].len)*sam[u].siz*sam[u].siz;
		sam[sam[u].lin].siz+=sam[u].siz,du[sam[u].lin]--;
		if(!du[sam[u].lin])que.push_back(sam[u].lin);
	}
	for(int i=1;i<=cnt;i++){
//		cout<<i<<":"<<sam[i].lin<<"-"<<sam[i].len<<" "<<sam[i].siz<<"-"<<(char)(sam[i].sumn+'a')<<"\n";
//		cout<<i<<" "<<sam[i].lin<<" "<<(char)(sam[i].sumn+'a')<<"\n";
		for(int j=0;j<=25;j++){
			if(!sam[i].to[j])continue;
//			cout<<i<<" "<<sam[i].to[j]<<" "<<(char)(sam[j].sumn+'a')<<"\n";
		}
		if(!du[i])que.push_back(i);
	}
	printf("%lld\n",ans);
	return ;
}
signed main(){
	int T=rd();
	while(T--)work();
	return 0;
}
/*
2
llalahaffl
jijhjjhhki
*/

动态构建后缀自动机,新增的后缀就是 sam[cur].len-sam[sam[cur].link].len

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}

const int N=100001;
struct node{
	unordered_map<int,int>to;
	int len,fro;
}sam[N*2];
int n,last,cnt;
long long ans;

void insert(int c){
	int p=last,now=++cnt;
	sam[now].len=sam[p].len+1;
	while(p&&(!sam[p].to[c]))sam[p].to[c]=now,p=sam[p].fro;
	last=now;
	if(!p)return sam[now].fro=1,void(0);
	if(sam[p].len+1==sam[sam[p].to[c]].len)return sam[now].fro=sam[p].to[c],void(0);
	int clone=++cnt,qnode=sam[p].to[c];
	sam[clone]=sam[qnode];
	sam[clone].len=sam[p].len+1;
	sam[qnode].fro=sam[now].fro=clone;
	while(p&&sam[p].to[c]==qnode)sam[p].to[c]=clone,p=sam[p].fro;
	return ;
}

signed main(){
	n=rd();
	last=cnt=1;
	for(int i=1;i<=n;i++){
		int x=rd();
		insert(x);
		ans+=sam[last].len-sam[sam[last].fro].len;
		printf("%lld\n",ans);
	}
//	for(int i=1;i<=cnt;i++)cout<<i<<":"<<sam[i].len<<" "<<sam[i].fro<<"\n";
	return 0;
}

很久之前写的代码,变量名可能不太一样。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=1000010;
char s[N];
int n,cnt=1;
int siz[N],lin[N],len[N],sum[N],to[N][26],num[N];
int t[N],A[N];
inline int insert(int p,int sumn){
	int cur=++cnt;
	siz[cur]=1,len[cur]=len[p]+1,num[cur]=sumn;
	while(p&&!to[p][sumn])to[p][sumn]=cur,p=lin[p];
	if(!p)return lin[cur]=1,cur;
	int q=to[p][sumn];
	if(len[p]+1==len[q])return lin[cur]=q,cur;
	int clone=++cnt;
	len[clone]=len[p]+1,lin[clone]=lin[q],num[clone]=num[q];
	memcpy(to[clone],to[q],sizeof(to[q]));
	lin[q]=lin[cur]=clone;
	while(p&&to[p][sumn]==q)to[p][sumn]=clone,p=lin[p];
	return cur;
}
void dfs(int u,int Sum){
	Sum-=siz[u];
	if(Sum<0)return ;
	if(u!=1)putchar(num[u]+'a');
	if(Sum<=0)return ;
	for(int i=0;i<26;i++){
		if(!to[u][i])continue;
		if(Sum>sum[to[u][i]])Sum-=sum[to[u][i]];
		else{
//			cout<<u<<":"<<(char)(i+'a')<<"-"<<to[u][i]<<" "<<sum[to[u][i]]<<"-"<<Sum<<"\n";
			dfs(to[u][i],Sum);
			break;
		}
	}
	return ;
}
signed main(){
	scanf("%s",s+1),n=strlen(s+1);
	int T=rd(),K=rd();
	for(int i=1,last=1;i<=n;i++)last=insert(last,s[i]-'a');
	for(int i=1;i<=cnt;i++)t[len[i]]++;
	for(int i=1;i<=n;i++)t[i]+=t[i-1];
	for(int i=cnt;i>=1;i--)A[t[len[i]]--]=i;
	for(int i=cnt;i>=1;i--)siz[lin[A[i]]]+=siz[A[i]];
	for(int i=1;i<=cnt;i++)sum[i]=(!T)?(siz[i]=1):siz[i];
	sum[1]=siz[1]=0;
	for(int i=cnt;i>=1;i--){
		int u=A[i];
		for(int j=0;j<26;j++){
			if(!to[u][j])continue;
			sum[u]+=sum[to[u][j]];
		}
	}
	if(sum[1]<K)return printf("-1\n"),0;
	dfs(1,K);
	return 0;
}
/*
aaaaa
1 15
*/

  • SP1811
    SAM在类似AC机方面的作用,是把所有子串插入AC机。
    但是要注意当前点的长度并不是匹配上的串的长度,但跳link之后的长度一定是匹配上的串的长度。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=500010;
struct node{
	int len,lin,to[26];
}sam[N];
int n,cnt=1,ans;
char s[N];
inline int insert(int p,int sumn){
	int cur=++cnt;
	sam[cur].len=sam[p].len+1;
	while(p&&!sam[p].to[sumn])sam[p].to[sumn]=cur,p=sam[p].lin;
	if(!p)return sam[cur].lin=1,cur;
	int q=sam[p].to[sumn];
	if(sam[p].len+1==sam[q].len)return sam[cur].lin=q,cur;
	int clone=++cnt;
	sam[clone]=sam[q],sam[clone].len=sam[p].len+1;
	sam[q].lin=sam[cur].lin=clone;
	while(p&&sam[p].to[sumn]==q)sam[p].to[sumn]=clone,p=sam[p].lin;
	return cur;
}
signed main(){
	scanf("%s",s+1),n=strlen(s+1);
	for(int i=1,last=1;i<=n;i++)last=insert(last,s[i]-'a');
	scanf("%s",s+1),n=strlen(s+1);
	for(int i=1,cur=1,len=0;i<=n;i++){
		while(cur&&!sam[cur].to[s[i]-'a'])cur=sam[cur].lin,len=sam[cur].len;
		if(cur)cur=sam[cur].to[s[i]-'a'],len++,ans=max(ans,len);
		else cur=1,len=0;
	}
	printf("%d\n",ans);
	return 0;
}

关键性质:反串的Link树是原串的后缀树。


考虑对SAM上endpos的维护,可以用线段树合并处理很多信息。


SAM上有个自然根号,即:若串长为 n ,在parent树上所有点的子节点个数之和为 nn 的。对广义sam也成立。
然后sam上endpos构成的等差数列也是 nn级别的。证明

posted @   flywatre  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示