【后缀自动机】【拓扑排序】【动态规划】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;
}
【后缀自动机】【拓扑排序】【动态规划】hihocoder1457 后缀自动机四·重复旋律7的更多相关文章
- BZOJ 后缀自动机四·重复旋律7
后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的 ...
- HDU_1457_后缀自动机四·重复旋律7
#1457 : 后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成 ...
- hiho一下123周 后缀数组四·重复旋律
后缀数组四·重复旋律4 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构成的数列.小Hi ...
- hihocoder #1419 : 后缀数组四·重复旋律4
#1419 : 后缀数组四·重复旋律4 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构 ...
- hihocoder 1457(后缀自动机+拓扑排序)
题意 给定若干组由数字构成的字符串,求所有不重复子串的和(把他们看成十进制),答案mod(1e9+7) 题解: 类似后缀数组的做法,把字符串之间用':'连接,这里用':'是因为':'的ascii码恰好 ...
- hihoCoder #1457 : 后缀自动机四·重复旋律7(后缀自动机 + 拓扑排序)
http://hihocoder.com/problemset/problem/1457 val[i] 表示状态i所表示的所有字符串的十进制之和 ans= ∑ val[i]在后缀自动机上,从起始状态走 ...
- HIHOcoder 1457 后缀自动机四·重复旋律7
思路 后缀自动机题目,题目本质上是要求求出所有不同的子串的和,SAM每个节点中存放的子串互不相同,所以对于每个节点的sum,可以发现是可以递推的,每个点对子节点贡献是sum[x]*10+c*sz[x] ...
- hihocoder 1457 后缀自动机四·重复旋律7 求不同子串的和
描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的是小Hi发现了一部名字叫<十进制进行曲大全>的作品集,顾名思义,这部作品集里有许多作品 ...
- hihocoder 1457 后缀自动机四·重复旋律7 ( 多串连接处理技巧 )
题目链接 分析 : 这道题对于单个串的用 SAM 然后想想怎么维护就行了 但是多个串下.可以先将所有的串用一个不在字符集( 这道题的字符集是 '0' ~ '9' ) 链接起来.建立后缀自动机之后 在统 ...
随机推荐
- CursorFileManager对cursor文件的读写
public class CursorFileManager implements CursorManager{public void write(String key, LongCursor cur ...
- Coursera在线学习---第九节(1).异常数据检测(Anomaly Detection)
一.如何构建Anomaly Detection模型? 二.如何评估Anomaly Detection系统? 1)将样本分为6:2:2比例 2)利用交叉验证集计算出F1值,可以用F1值选取概率阈值ξ,选 ...
- js上传文件(图片)限制格式及大小为3M
本文保存为.html文件用浏览器打开即可测试功能 <form id="form1" name="form1" method="post&qu ...
- windows server 2012 IIS配置之FTP站点
原文地址:[原创]winserver2012IIS配置之FTP站点作者:hkmysterious 一.实验拓扑: 使server2012客户计算机通过ftp方式从FTP服务器上下载已上传并共享的文 ...
- recv函数的MSG_PEEK标志介绍
考虑下面的场景,server向client发送数据"_META_DATA_\r\n_USER_DATA_",要求"\r\n"之前的数据_META_DATA_在第 ...
- maven repository 配置
eclipse maven 配置修改: maven repository 配置 http://blog.csdn.net/joewolf/article/details/4876604 Maven缺省 ...
- Eclipse中部署ES源码运行
https://stackoverflow.com/questions/40924671/how-to-build-elasticsearch-source-code-using-gradle Gra ...
- Spring boot 集成hessian - LocalDateTime序列化和反序列化
- 反序列化 import com.caucho.hessian.HessianException; import com.caucho.hessian.io.AbstractDeserializer ...
- hdu 1203(01背包)被初始化坑惨了
I NEED A OFFER! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)T ...
- python 几种循环性能测试: while, for, 列表生成式, map等
直接上代码: #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/07/24 16:23 import itertools imp ...