hiho一下第128周 后缀自动机二·重复旋律5
#1445 : 后缀自动机二·重复旋律5
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。
现在小Hi想知道一部作品中出现了多少不同的旋律?
解题方法提示
输入
共一行,包含一个由小写字母构成的字符串。字符串长度不超过 1000000。
输出
一行一个整数,表示答案。
- 样例输入
-
aab
- 样例输出
-
5
解题方法提示
小Hi:本周的题目其实就是给定一个字符串S,要求出S的所有不同子串的数目。小Ho你知道如何快速求解么?
小Ho:我们最近在讨论后缀自动机,所以肯定是和后缀自动机有关!根据上周学习的SAM的基本概念和性质,SAM的每个状态st都包含了一部分S的子串,记作substrings(st),并且(1)对于两个不同状态u和v,包含的子串substrings(u) ∩ substrings(v) = ∅; (2)每个子串都恰好被一个状态包含。所以我们只要构造出来S对应的SAM,再对所有状态st求Σ(maxlen(st)-minlen(st))就是子串的数目。
小Hi:没错。上周我们提到SAM有O(length(S))的构造法。这周我们就来讲一讲如何构造。
小Hi:首先,为了实现O(length(S))的构造,我们对于每个状态不能保存太多数据。例如substring(st)肯定是没法保存下来了。对于状态st我们只保存如下数据:
数据 含义 maxlen[st] st包含的最长子串的长度 minlen[st] st包含的最短字串的长度 trans[st][] st的转移函数 slink[st] st的Suffix Link 小Hi:其次,我们用增量法构造S对应的SAM。我们从初始状态开始,每次添加一个字符S[1], S[2], ... S[N],依次构造可以识别S[1], S[1..2], S[1..3], ... S[1..N]=S的SAM。
小Hi:假设我们已经构造好了S[1..i]的SAM。这时我们要添加字符S[i+1],于是我们新增了i+1个S[i+1]的后缀要识别:S[1..i+1], S[2..i+1], ... S[i..i+1], S[i+1]。 考虑到这些新增状态分别是从S[1..i], S[2..i], S[3..i], ... , S[i], ""(空串)通过字符S[i+1]转移过来的,所以我们还要对S[1..i], S[2..i], S[3..i], ... , S[i], ""(空串)对应的状态们增加相应的转移。
小Hi:我们假设S[1..i]对应的状态是u,等价于S[1..i]∈ substrings(u)。根据上周的讨论我们知道S[1..i], S[2..i], S[3..i], ... , S[i], ""(空串)对应的状态们恰好就是从u到初始状态S的由Suffix Link连接起来路径上的所有状态,不妨称这条路径(上所有状态集合)是suffix-path(u->S)。
小Hi:显然至少S[1..i+1]这个子串不能被以前的SAM识别,所以我们至少需要添加一个状态z,z至少包含S[1..i+1]这个子串。
小Hi:首先考虑一种最简单的情况:对于suffix-path(u->S)的任意状态v,都有trans[v][S[i+1]]=NULL。这时我们只要令trans[v][S[i+1]]=z,并且令slink[st]=S即可。
小Hi:例如我们已经得到"aa"的SAM,现在希望构造"aab"的SAM。如下图所示:
小Hi:此时u=2,z=3,suffix-path(u->S)是桔色状态组成的路径2-1-S。并且这3个状态都没有对应字符b的转移。所以我们只要添加红色转移trans[2][b]=trans[1][b]=trans[S][b]=z即可。当然也不要忘了slink[3]=S。
小Ho:那要是suffix-path(u->S)上有一个节点v,使得trans[v][S[i+1]]!=NULL怎么办?
小Hi:好问题。我们以下图为例,假设我们已经构造"aabb"的SAM如图,现在我们要增加一个字符a构造"aabba"的SAM。
小Hi:这时u=4,z=6,suffix-path(u->S)是桔色状态组成的路径4-5-S。对于状态4和状态5,由于它们都没有对应字符a的转移,所以我们只要添加红色转移trans[4][a]=trans[5][a]=z=6即可。面对S时我们遇到了小Ho你提出的问题,trans[S][a]=1已经存在,怎么办?
小Ho:怎么办呢?
小Hi:不失一般性,我们可以认为在suffix-path(u->S)遇到的第一个状态v满足trans[v][S[i+1]]=x。这时我们需要讨论x包含的子串的情况。如果x中包含的最长子串就是v中包含的最长子串接上字符S[i+1],等价于maxlen(v)+1=maxlen(x),比如在上面的例子里,v=S, x=1,longest(v)是空串,longest(1)="a"就是longest(v)+'a'。这种情况比较简单,我们只要增加slink[z]=x即可。
小Hi:如果x中包含的最长子串 不是 v中包含的最长子串接上字符S[i+1],等价于maxlen(v)+1 < maxlen(x),这种情况最为复杂,不失一般性,我们用下图表示这种情况,这时增加的字符是c,状态是z。
小Hi:在suffix-path(u->S)这条路径上,从u开始有一部分连续的状态满足trans[u..][c]=NULL,对于这部分状态我们只需增加trans[u..][c]=z。紧接着有一部分连续的状态v..w满足trans[v..w][c]=x,并且longest(v)+c不等于longest(x)。这时我们需要从x拆分出新的状态y,并且把原来x中长度小于等于longest(v)+c的子串分给y,其余字串留给x。同时令trans[v..w][c]=y,slink[y]=slink[x], slink[x]=slink[z]=y。
小Ho:好像比较复杂。
小Hi:我们来举个例子。假设我们已经构造"aab"的SAM如图,现在我们要增加一个字符b构造"aabb"的SAM。
小Hi:当我们处理在suffix-path(u->S)上的状态S时,遇到trans[S][b]=3。并且longest(3)="aab",longest(S)+'b'="b",两者不相等。其实不相等意味增加了新字符后endpos("aab")已经不等于endpos("b"),势必这两个子串不能同属一个状态3。这时我们就要从3中新拆分出一个状态5,把"b"及其后缀分给5,其余的子串留给3。同时令trans[S][c]=5, slink[5]=slink[3]=S, slink[3]=slink[6]=5。
小Hi:整个过程的代码如下,其中状态0代表初始状态S;状态u, v, x, y, z的意义如上文所述;-1代表slink或者trans不存在。
const int MAXL = 1000000; string s; int n = 0, len, st; int maxlen[2 * MAXL + 10], minlen[2 * MAXL + 10], trans[2 * MAXL + 10][26], 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 < 26; i++) {if(_trans == NULL)trans[n][i] = -1;elsetrans[n][i] = _trans[i];}slink[n] = _slink;return n++; }int add_char(char ch, int u) {int c = ch - 'a';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]) { //较简单的情况,不用拆分xminlen[z] = maxlen[x] + 1;slink[z] = x;return z;}int y = new_state(maxlen[v] + 1, -1, trans[x], slink[x]); //最复杂的情况,拆分xslink[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; }
小Ho:咦?程序倒是意外的简单。
#include <iostream> #include <cstring> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <time.h> #include <string> #include <map> #include <stack> #include <vector> #include <set> #include <queue> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int N=1e6+100; const int M=1e6+5; int tot,slink[2*N],trans[2*N][26],minlen[2*N],maxlen[2*N]; char str[N]; int n; int newstate(int _maxlen,int _minlen,int* _trans,int _slink) {maxlen[++tot]=_maxlen;minlen[tot]=_minlen;slink[tot]=_slink;if(_trans)for(int i=0; i<26; i++)trans[tot][i]=_trans[i];return tot; } int add_char(char ch,int u) {int c=ch-'a',v=u;int z=newstate(maxlen[u]+1,-1,NULL,0);while(v&&!trans[v][c]) {trans[v][c]=z;v=slink[v];}if(!v) {minlen[z]=1;slink[z]=1;return z;}int x=trans[v][c];if(maxlen[v]+1==maxlen[x]) {slink[z]=x;minlen[z]=maxlen[x]+1;return z;}int y=newstate(maxlen[v]+1,-1,trans[x],slink[x]);slink[z]=slink[x]=y;minlen[x]=minlen[z]=maxlen[y]+1;while(v&&trans[v][c]==x) {trans[v][c]=y;v=slink[v];}minlen[y]=maxlen[slink[y]]+1;return z; } int main() {scanf("%s",str);int len=strlen(str),pre=1;tot=1;for(int i=0; i<len; i++) {pre=add_char(str[i],pre);}long long ans=0;for(int i=2; i<=tot; i++) {ans+=maxlen[i]-minlen[i]+1;}cout<<ans<<endl;return 0; }
转载于:https://www.cnblogs.com/jianrenfang/p/6341455.html
hiho一下第128周 后缀自动机二·重复旋律5相关推荐
- hihoCoder #1445 : 后缀自动机二·重复旋律5
描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi想知道一部作品中出现了多少不同的旋律? 输入 共一行,包含一个由小写字母构成的字符串.字符串长度不 ...
- HihoCoder - 1445 后缀自动机二·重复旋律5(后缀自动机)
题目链接:点击查看 题目大意:给出一个字符串 s ,求 s 中本质不同的子串的数量 题目分析:因为 s 的长度给到了 1e6 ,用后缀数组可能会超时,所以最好的方法肯定是用后缀自动机来做,因为每个节点 ...
- hihocoder 1465 : 后缀自动机五·重复旋律8(后缀自动机+最长公共子串)
1465 : 后缀自动机五·重复旋律8 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的 ...
- hihocoder 1457 : 后缀自动机四·重复旋律7(后缀自动机+拓扑序BFS)
#1457 : 后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成 ...
- hihocoder 1449 : 后缀自动机三·重复旋律6(后缀自动机)
#1449 : 后缀自动机三·重复旋律6 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数 ...
- hiho一下120周 后缀数组一·重复旋律
后缀数组一·重复旋律 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构成的数列. 小Hi ...
- hihoCoder #1457 : 后缀自动机四·重复旋律7
题目链接 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的是小Hi发现了一部名字叫<十进制进行曲大全>的作品集,顾名思义,这部作品集里 ...
- hihoCoder #1449 : 后缀自动机三·重复旋律6
题目链接 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi想知道一部作品中 ...
- HihoCoder - 1465 后缀自动机五·重复旋律8(后缀自动机)
题目链接:点击查看 题目大意:给出一个模式串,在给出多个匹配串,问每个匹配串与其"循环相似旋律"的字符串,在模式串中出现的总次数,"循环相似旋律"指的是一个字符 ...
最新文章
- 自定义apt升级脚本
- c的开始,求最大数。
- 以列表形式输出_python格式化输出总结
- mybatis更新Blob类型字段要用updateByPrimaryKeyWithBLOBs
- 把握数据库发展趋势 DBA应如何避免“踩坑”?
- android长按home键关闭程序,应用程序退出后Dialog弹出
- HTMLCSS————CSS常用选择器及优先级
- C/C++获取本地IP(适用于多种操作系统)
- 大数据_Flink_Java版_ProcessFunction(4)_应用案例_高低温分流---Flink工作笔记0069
- Windows 10 JDK安装及环境配置(vim+gcc)
- Brute Force_medium
- 云服务器更换系统要钱吗,云服务器可以更换系统吗
- C#实现多功能记事本
- 成都哪所专科院校有计算机专业,成都哪些高职院校有计算机应用技术
- 阿里轻量应用服务器搭建ftp服务器
- 邮件退信RCPT TO command问号
- 百度键盘计算机,彻底删除电脑中流氓软件的4大实用高招! 为什么电脑会被偷偷装上百度软件?...
- Qt Quick 如何入门?
- ChatGPT解开了我一直以来对自动化测试的疑惑
- PowerShell操作DNS创建SRV记录
热门文章
- mysql主从同步原理_mysql主从同步以及原理
- 我的世界服务器虚拟菜单插件,[综合|娱乐|管理][比赛]VirtualMenu——支持多种类型GUI的虚拟菜单[1.7.10~1.14]...
- python加密敏感信息_仅需10行代码,使用python加密用户敏感数据
- 伪代码block转换成程序流程图_程序设计基础
- html5中Canvas、绘制线条模糊、常见绘制工具、绘制基本图形、绘制图片、面向对象的方式绘制图形图片、绘制文本、帧动画绘制
- 分布式Zookeeper安装搭建详解
- 最优化学习笔记(十三)——基本共轭方向算法(扩张子空间定理)
- router vue 动态改变url_vue里router-link标签设置动态路由的3个方法
- 负数如何归一化处理_机器学习之数据预处理
- 互联网环境下分布式事务处理系统现状与趋势