【后缀自动机】【拓扑排序】【动态规划】hihocoder1457 后缀自动机四·重复旋律7

解题方法提示

小Hi:我们已经学习了后缀自动机,今天我们再来看这道有意思的题。

小Ho:好!这道题目让我们求的是若干的数字串所有不同子串的和。

小Hi:你能不能结合后缀自动机的性质来思考如何解决本题?

小Ho:这道题目既然是关于子串,那么我知道从后缀自动机的所有状态中包含的子串的集合恰好对应原串的所有不重复子串。

小Hi:很好。那你可以先简化问题,想想只有一个串怎么做?

小Ho:好的。这个难不倒我。我上次已经知道如何计算一个串所有不同子串的数量,现在这题也类似,只不过计算更加复杂一点。

小Hi:那你可以详细说说。

小Ho:我们举个例子,假设S="1122124",其实就是我们熟悉的例子"aabbabd"啦。

状态 子串 endpos sum
S 空串   0
1 1 {1,2,5} 1
2 11 {2} 11
3 112 {3} 112
4 1122,122,22 {4} 1266
5 2 {3,4,6} 2
6 11221,1221,221,21 {5} 12684
7 112212,12212,2212,212 {6} 126848
8 12 {3,6} 12
9 1122124,122124,22124,2124,124,24,4 {7} 1248648

小Ho:如果我们能像上面的表格一样求出每个状态中包含的子串的"和",不妨记为sum(st)。那么我们只要求出Σsum(st)就是答案了。

小Hi:那你讲讲怎么求出每个状态的和?

小Ho:从初始状态开始一个个递推出来咯。比如我们现在要求状态6也就是{11221,1221,221,21}的和。我们知道到达状态6的边(transition)有2条,分别是trans[4][1]和trans[5][1]。如果我们已经求出sum(4) = 1266, sum(5)=2,那么我们就可以求出sum(6)=(sum(4) * 10 + 1 * |substrings(4)|]) + (sun(5) * 10 + 1 * |substring(5)|) = (12660 + 1 * 3) + (2 * 10 + 1 * 1) = 12684。

小Ho:换句话说,状态6里的{11221, 1221, 221}这三个子串是从状态4的所有(3个)子串乘以10再加1得到的;状态6里的{21}这个子串是从状态5的所有(1个)子串乘以10再加1得到的。也就是说对于状态st

sum(st) = Σ{sum(x) * 10 + c * |substrings(x)| | trans[x][c] = st}。

小Ho:我们知道SAM的状态和转移构成了一个有向无环图,我们只要求出状态的拓扑序,依次求出sum(st)即可。

小Hi:不错嘛。那我们回到原题的多个串的情况,怎么解决?

小Ho:多个串我就不会了 ┑( ̄Д  ̄)┍

小Hi:还记得我们第122周用后缀数组求多个串的最长公共字串时用到的技巧么?

小Ho:把多个串用‘#‘连接起来当作一个串来处理?

小Hi:没错。这次我们也使用这种方法,把所有串用冒号‘:‘ (‘:‘的ACII码是58,也就是‘0‘的ASCII码+10,方便处理) 连接以来。以两个串"12"和"234"为例,"12:234"的SAM如图:

 ‘

状态 子串 endpos |valid-substrings| sum
S 空串   1 0
1 1 {1} 1 1
2 12 {2} 1 12
3 12:,2:,: {3} 0 0
4 12:2,2:2,:2 {4} 0 0
5 2 {2,4} 1 2
6 12:23,2:23,:23,23,3 {5} 2 26
7 12:234,2:234,:234,234,34,4 {6} 3 272

小Ho:看上去如果我们把每个状态中带冒号的子串都排除掉,好像也是可以递推的!

小Hi:没错。如果我们用valid-substrings(st)表示一个状态中所有的不带冒号的子串,那么对于sum(st)我们有类似的递推式

sum(st) = Σ{sum(x) * 10 + c * |valid-substrings(x)| | trans[x][c] = st}

小Ho:那么关键就是|valid-substrings(st)|怎么求出来了?

小Hi:没错。|valid-substrings(st)|代表st中不带冒号的子串个数,这个值恰好就是从初始状态S到状态st的所有"不经过冒号转移的边"的路径数目。

小Ho:好像有点绕。

小Hi:举个例子,对于状态6,如果我们不经过标记为‘:‘的转移,那么从S到状态6一共有2条路径,是S->6和S->5->6,分别对应不带冒号的子串3和23。前面已经提到过SAM的状态和转移构成了一个有向无环图,有向无环图上的路径数目也是一个经典的拓扑排序问题,可以参考之前我们的讨论

小Ho:我明白了。建完SAM之后对所有状态拓扑排序,然后按拓扑序递推一边求出|valid-substrings(st)|,一边求出sum(st)就可以了。好了,我写程序去了。

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
#define MOD 1000000007ll
#define MAXL 2000000
#define MAXC 11
char s[MAXL+10];
int len/*文本串长度*/;
struct SAM{
	int n/*状态数0~n-1*/,maxlen[2*MAXL+10],minlen[2*MAXL+10],trans[2*MAXL+10][MAXC],slink[2*MAXL+10];
	int new_state(int _maxlen,int _minlen,int _trans[],int _slink){
		maxlen[n]=_maxlen;
		minlen[n]=_minlen;
		for(int i=0;i<MAXC;++i){
			if(_trans==NULL){
				trans[n][i]=-1;
			}
			else{
				trans[n][i]=_trans[i];
			}
		}
		slink[n]=_slink;
		return n++;
	}
	int add_char(char ch,int u){
		if(u==-1){
			return new_state(0,0,NULL,-1);
		}
		int c=ch-‘0‘;
		int z=new_state(maxlen[u]+1,-1,NULL,-1);
		int v=u;
		while(v!=-1 && trans[v][c]==-1){
			trans[v][c]=z;
			v=slink[v];
		}
		if(v==-1){//最简单的情况,suffix-path(u->S)上都没有对应字符ch的转移
			minlen[z]=1;
			slink[z]=0;
			return z;
		}
		int x=trans[v][c];
		if(maxlen[v]+1==maxlen[x]){//较简单的情况,不用拆分x
			minlen[z]=maxlen[x]+1;
			slink[z]=x;
			return z;
		}
		int y=new_state(maxlen[v]+1,-1,trans[x],slink[x]);//最复杂的情况,拆分x
		slink[y]=slink[x];
		minlen[x]=maxlen[y]+1;
		slink[x]=y;
		minlen[z]=maxlen[y]+1;
		slink[z]=y;
		int w=v;
		while(w!=-1 && trans[w][c]==x){
			trans[w][c]=y;
			w=slink[w];
		}
		minlen[y]=maxlen[slink[y]]+1;
		return z;
	}
}sam;
int m;
queue<int>q;
int ru[MAXL*2+10];
ll paths[MAXL*2+10],f[MAXL*2+10],ans;
int main(){
//	freopen("hihocoder1457.in","r",stdin);
	scanf("%d",&m);
	for(int i=1;i<=m;++i){
		scanf("%s",s+len);
		len=strlen(s);
		s[len]=‘:‘;
		s[++len]=‘\0‘;
	}
	int U=sam.add_char(0,-1);
	for(int i=0;i<len;++i){
		U=sam.add_char(s[i],U);
	}
	for(int i=0;i<sam.n;++i){
		for(int j=0;j<MAXC;++j){
			if(sam.trans[i][j]!=-1){
				++ru[sam.trans[i][j]];
			}
		}
	}
	paths[0]=1;
	for(int i=0;i<sam.n;++i){
		if(!ru[i]){
			q.push(i);
		}
	}
	while(!q.empty()){
		U=q.front(); q.pop();
		for(int i=0;i<MAXC;++i){
			if(i!=‘:‘-‘0‘){
				paths[sam.trans[U][i]]+=paths[U];
				f[sam.trans[U][i]]=(f[sam.trans[U][i]]+(f[U]*10ll%MOD+(ll)i*(paths[U]%MOD)%MOD)%MOD)%MOD;
			}
			--ru[sam.trans[U][i]];
			if(!ru[sam.trans[U][i]]){
				q.push(sam.trans[U][i]);
			}
		}
	}
	for(int i=0;i<sam.n;++i){
		ans=(ans+f[i])%MOD;
	}
	printf("%lld\n",ans);
	return 0;
}
时间: 2024-10-11 16:55:24

【后缀自动机】【拓扑排序】【动态规划】hihocoder1457 后缀自动机四·重复旋律7的相关文章

hihocoder #1457 : 后缀自动机四&#183;重复旋律7

#1457 : 后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的是小Hi发现了一部名字叫<十进制进行曲大全>的作品集,顾名思义,这部作品集里有许多作品,但是所有的作品有一个共同特征:只用了十个音符,所有的音符都表示成0-9的数字. 现在小Hi想知道这部作品中所有不同的旋律的“和”(也就是把串看成数字,在十进制下的求和,允许有前导0).答案有可能

BZOJ 后缀自动机四&#183;重复旋律7

后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的是小Hi发现了一部名字叫<十进制进行曲大全>的作品集,顾名思义,这部作品集里有许多作品,但是所有的作品有一个共同特征:只用了十个音符,所有的音符都表示成0-9的数字. 现在小Hi想知道这部作品中所有不同的旋律的“和”(也就是把串看成数字,在十进制下的求和,允许有前导0).答案有可能很大,我们需要对

后缀自动机四&#183;重复旋律7

后缀自动机四·重复旋律7 1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 2000010; 4 const int mod = 1e9 + 7; 5 char s[maxn]; 6 int len[maxn<<1], tr[maxn<<1][11], link[maxn<<1]; 7 int sz, last; 8 void init(){ 9 sz = last =

hiho一下123周 后缀数组四&#183;重复旋律

后缀数组四·重复旋律4 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构成的数列.小Hi在练习过很多曲子以后发现很多作品中的旋律有重复的部分. 我们把一段旋律称为(k,l)-重复的,如果它满足由一个长度为l的字符串重复了k次组成. 如旋律abaabaabaaba是(4,3)重复的,因为它由aba重复4次组成. 小Hi想知道一部作品中k最大的(k,l)-重复旋律. 解题方法提示 输入 一

后缀数组四&#183;重复旋律4

后缀数组四·重复旋律4 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构成的数列.小Hi在练习过很多曲子以后发现很多作品中的旋律有重复的部分. 我们把一段旋律称为(k,l)-重复的,如果它满足由一个长度为l的字符串重复了k次组成. 如旋律abaabaabaaba是(4,3)重复的,因为它由aba重复4次组成. 小Hi想知道一部作品中k最大的(k,l)-重复旋律. 解题方法提示 输入 一

hihocoder 1457(后缀自动机+拓扑排序)

题意 给定若干组由数字构成的字符串,求所有不重复子串的和(把他们看成十进制),答案mod(1e9+7) 题解: 类似后缀数组的做法,把字符串之间用':'连接,这里用':'是因为':'的ascii码恰好是9的下一个 然后建立后缀自动机. 之后把其实只要把其中的所有':'边删去,就可以进行转移了 如果x连向了y,边权是c,那么有转移 dp[y] += dp[x]*10 + c*sz[x] 所以只要拓扑排序一下就好 (写这题wa了好几次,主要是在删边建立新图的过程出了问题) #include <ios

hihocoder 1457 后缀自动机四&#183;重复旋律7 求不同子串的和

描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的是小Hi发现了一部名字叫<十进制进行曲大全>的作品集,顾名思义,这部作品集里有许多作品,但是所有的作品有一个共同特征:只用了十个音符,所有的音符都表示成0-9的数字. 现在小Hi想知道这部作品中所有不同的旋律的“和”(也就是把串看成数字,在十进制下的求和,允许有前导0).答案有可能很大,我们需要对(10^9 + 7)取摸. 解题方法提示 × 解题方法提示 小Hi:我们已经学习了后缀自动机,今天

Luogu P3953【NOIP2017】逛公园【最短路+拓扑排序+动态规划】

题目描述 策策同学特别喜欢逛公园.公园可以看成一张NN个点MM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,NN号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. 策策每天都会去逛公园,他总是从1号点进去,从NN号点出来. 策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间.如果1号点 到NN号点的最短路长为dd,那么策策只会喜欢长度不超过d + Kd+K的路线. 策策

BZOJ 2938 Poi2000 病毒 AC自动机+拓扑排序

题目大意:给定n个01串,问是否存在一个无限长的01串,不包含这n个01串中的任何一个 建出Trie图之后判环即可 我这傻逼一开始居然跑了一个DFS去判环23333 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 30300 using namespace std; int n; char s[M]; namespace Aho_C