hihocoder 后缀自动机

link

重复旋律5

求不同子串个数,SAM后统计所有节点的mx-mi+1即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 26;
const int maxn = 1e6+5;
namespace SAM{
    
    
	struct SamNode{
    
    int trans[SZ],slink,mi,mx;}sam[maxn<<1];
	int tot;	char s[maxn];
	int newnode(int mi,int mx,int *trans,int slink) {
    
    
		sam[tot].mi=mi;
		sam[tot].mx=mx;
		sam[tot].slink=slink;
		trans?memcpy(sam[tot].trans,trans,SZ*sizeof(int)):memset(sam[tot].trans,-1,SZ*sizeof(int));
		return tot++;
	}
	int append(int ch,int u) {
    
    
		int c=s[ch]-'a';
		int z=newnode(-1,sam[u].mx+1,0,-1);
		int v=u;
		while(v!=-1&&sam[v].trans[c]==-1) {
    
    
			sam[v].trans[c]=z;
			v=sam[v].slink;
		}
		if(v==-1) {
    
    
			sam[z].slink=0;
			sam[z].mi=1;
		} else {
    
    
			int x=sam[v].trans[c];
			if(sam[v].mx+1==sam[x].mx) {
    
    
				sam[z].mi=sam[x].mx+1;
				sam[z].slink=x;
			} else {
    
    
				int y=newnode(-1,sam[v].mx+1,sam[x].trans,sam[x].slink);
				sam[x].slink=y;
				sam[x].mi=sam[y].mx+1;
				sam[z].slink=y;
				sam[z].mi=sam[y].mx+1;
				while(v!=-1&&sam[v].trans[c]==x) {
    
    
					sam[v].trans[c]=y;
					v=sam[v].slink;
				}				
				sam[y].mi=sam[sam[y].slink].mx+1;
			}
		}
		return z;
	}
};

int main() {
    
    
	scanf("%s",SAM::s+1);
	int u=SAM::newnode(0,0,0,-1);
	int n=strlen(SAM::s+1);
	for(int i=1;i<=n;i++) {
    
    
		u=SAM::append(i,u);
	}
	ll ans=0;
	for(int i=1;i<SAM::tot;i++) {
    
    
		ans=ans+SAM::sam[i].mx-SAM::sam[i].mi+1;
	}
	printf("%lld\n",ans);
	return 0;

}

重复旋律6

统计长度为1~n的所有子串的最多出现次数。
相当于求SAM每个节点的endpos集合大小,来更新该长度为节点mi~mx的ans(可以直接更新ans[mx],然后取后缀max,长度短的一定比长的出现次数多)
建slink_tree后dp,将节点分为两种:
1)仅右子节点集合构成
2)1)+ 字符串前缀(不可能出现在子节点中)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 6;
const int SZ = 26;

namespace SAM {
    
    
	struct SamNode{
    
    int mi,mx,slink,trans[SZ];}sam[maxn<<1];
	char s[maxn]; int tot;	ll dp[maxn<<1],ans[maxn<<1];
	int newnode(int mi,int mx,int *trans,int slink) {
    
    
		sam[tot].mi=mi;		sam[tot].mx=mx;		sam[tot].slink=slink;
		trans?memcpy(sam[tot].trans,trans,SZ*sizeof(int)):memset(sam[tot].trans,-1,SZ*sizeof(int));
		return tot++;
	}
	int append(int i,int u) {
    
    
		char c=s[i]-'a';
		int z=newnode(-1,sam[u].mx+1,0,-1);		dp[z]=1;
		int v=u;
		while(v!=-1&&sam[v].trans[c]==-1) {
    
    
			sam[v].trans[c]=z;
			v=sam[v].slink;
		}
		if(v==-1) {
    
    
			sam[z].slink=0;
			sam[z].mi=1;
		} else {
    
    
			int x=sam[v].trans[c];
			if(sam[v].mx+1==sam[x].mx) {
    
    
				sam[z].slink=x;
				sam[z].mi=sam[x].mx+1;
			} else {
    
    
				int y=newnode(-1,sam[v].mx+1,sam[x].trans,sam[x].slink);	dp[y]=0;
				sam[x].slink=y;		sam[x].mi=sam[y].mx+1;		
				sam[z].slink=y;		sam[z].mi=sam[y].mx+1;
				while(v!=-1&&sam[v].trans[c]==x) {
    
    
					sam[v].trans[c]=y;
					v=sam[v].slink;
				}
				sam[y].mi=sam[sam[y].slink].mx+1;
			}
		}
		return z;
	}
	vector<int> G[maxn<<1];
	void slink_tree() {
    
    
		for(int i=1;i<tot;i++) G[i].clear();
		for(int i=1;i<tot;i++) G[sam[i].slink].push_back(i);
	}
	void dfs(int u) {
    
    
		for(int v:G[u]) {
    
    
			dfs(v);
			dp[u]+=dp[v];
		}
	}
	void calu() {
    
    
		slink_tree();
		dfs(0);
		for(int i=1;i<tot;i++) ans[sam[i].mx]=max(ans[sam[i].mx],dp[i]);
		int n=strlen(s+1);
		for(int i=n-1;i>=1;i--) ans[i]=max(ans[i+1],ans[i]);
		for(int i=1;i<=n;i++) printf("%lld\n",ans[i]); 
	}
};
int main() {
    
    
	scanf("%s",SAM::s+1);
	int n=strlen(SAM::s+1),u=SAM::newnode(0,0,0,-1);
	for(int i=1;i<=n;i++) {
    
    
		u=SAM::append(i,u);
	}
	SAM::calu();
}

重复旋律7

(SAM 本身的转移函数trans为一个)
求多个串(‘0’~‘9’)的不同子串之和。
考虑如何求单个串的不同子串之和:
假如所有能够通过读入字符转移到状态i的状态的答案已经求得,那么状态i的答案即为 sigma(ANSj10+NUMjc)。拓扑序dp即可
多个串的情况则可以通过一个字符将他们链接起来,那么在SAM中,只需将每个状态代表的子串中出现这个新加入的字符忽略掉就可以。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 6;
const int mod = 1e9 + 7;
const int SZ = 11;

namespace SAM {
    
    
	struct SamNode{
    
    int mi,mx,slink,trans[SZ];}sam[maxn<<1];
	char s[maxn]; int tot;	
	int newnode(int mi,int mx,int *trans,int slink) {
    
    
		sam[tot].mi=mi;		sam[tot].mx=mx;		sam[tot].slink=slink;
		trans?memcpy(sam[tot].trans,trans,SZ*sizeof(int)):memset(sam[tot].trans,-1,SZ*sizeof(int));
		return tot++;
	}
	int append(int i,int u) {
    
    
		char c=s[i]-'0';
		int z=newnode(-1,sam[u].mx+1,0,-1);
		int v=u;
		while(v!=-1&&sam[v].trans[c]==-1) {
    
    
			sam[v].trans[c]=z;
			v=sam[v].slink;
		}
		if(v==-1) {
    
    
			sam[z].slink=0;
			sam[z].mi=1;
		} else {
    
    
			int x=sam[v].trans[c];
			if(sam[v].mx+1==sam[x].mx) {
    
    
				sam[z].slink=x;
				sam[z].mi=sam[x].mx+1;
			} else {
    
    
				int y=newnode(-1,sam[v].mx+1,sam[x].trans,sam[x].slink);	
				sam[x].slink=y;		sam[x].mi=sam[y].mx+1;		
				sam[z].slink=y;		sam[z].mi=sam[y].mx+1;
				while(v!=-1&&sam[v].trans[c]==x) {
    
    
					sam[v].trans[c]=y;
					v=sam[v].slink;
				}
				sam[y].mi=sam[sam[y].slink].mx+1;
			}
		}
		return z;
	}
	queue<int>q;	int in[maxn<<1];
	ll num[maxn<<1],dp[maxn<<1];
	void top_valid() {
    
    
		for(int i=0;i<tot;i++) {
    
    
			for(int j=0;j<SZ;j++) if(sam[i].trans[j]!=-1) 
				in[sam[i].trans[j]]++;
		}
		while(!q.empty()) q.pop();
		q.push(0) ; 	num[0]=1;
		while(!q.empty()) {
    
    
			int f=q.front();q.pop();
			for(int i=0;i<SZ;i++) {
    
    
				int v=sam[f].trans[i];
				if(v==-1) continue;
				in[v]--;
				if(!in[v]) q.push(v);
				if(i!=10) (num[v]+=num[f])%=mod;
			}
		}
	}
	void top_ans() {
    
    
		for(int i=0;i<tot;i++) {
    
    
			for(int j=0;j<SZ;j++) if(sam[i].trans[j]!=-1) 
				in[sam[i].trans[j]]++;
		}
		while(!q.empty()) q.pop();
		q.push(0);		dp[0]=0;
		while(!q.empty()) {
    
    
			int f=q.front();q.pop();
			for(int i=0;i<SZ;i++) {
    
    
				int v=sam[f].trans[i];
				if(v==-1) continue;
				in[v]--;
				if(!in[v]) q.push(v);
				if(i!=10) dp[v]=(dp[v]+dp[f]*10%mod+num[f]*i)%mod;
			}
		}
	}

	void calu() {
    
    
		top_valid();
		top_ans();
		ll ans=0;
		for(int i=1;i<tot;i++) ans=(ans+dp[i])%mod;
		printf("%lld\n",ans);
	}
};
char s[maxn];
int main() {
    
    
	int n,cnt=0;scanf("%d",&n);
	for(int i=1;i<=n;i++) {
    
    
		scanf("%s",s);
		int len=strlen(s);
		for(int j=0;j<len;j++) SAM::s[++cnt]=s[j]; 
		SAM::s[++cnt]='9'+1;
	}
	int u=SAM::newnode(0,0,0,-1);
	for(int i=1;i<=cnt;i++) {
    
    
		u=SAM::append(i,u);
	}
	SAM::calu();
}

猜你喜欢

转载自blog.csdn.net/qq_43914084/article/details/107151375
今日推荐