HihoCoder1465 重复旋律8(后缀自动机)
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。
小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”。
小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品。对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多次计)和该旋律是“循环相似旋律”。
输入
第一行,一个由小写字母构成的字符串S,表示一部音乐作品。字符串S长度不超过100000。
第二行,一个整数N,表示有N段旋律。接下来N行,每行包含一个由小写字母构成的字符串str,表示一段旋律。所有旋律的长度和不超过 100000。
输出
输出共N行,每行一个整数,表示答案。
- 样例输入
-
abac
3
a
ab
ca - 样例输出
-
2
2
1
现在我们要处理T的循环同构串们。这里有一个常用的技巧,假设T的长度是n,我们令T'=T + T[1..n-1]形成一个新的串T'。例如对于"abcd",我们把"abc"拼在"abcd"后面,得到新的T="abcdabc"。这样"abcd"的循环同构串就变成了T'="abcdabc"的长度为n的子串。
小Ho:哦!然后我们再用之前讲的方法求出在每个位置T'[i]结束的最长公共子串。我们可以求出对应的(u, l),如果这时l>=n,那我们就得到了一个公共子串T'[i-l+1 .. i]。这个子串在S中出现的次数是|endpos(u)|,又恰好包含T的循环同构串T'[i-n+1 .. i]。
小Hi:基本思路是对的。但是要注意处理两个特殊情况。第一个情况是T的n个循环同构子串有重复(相同)的情况。比如T="aa",T'="aaa",还是以S="aabbabd"为例
S: aabbabd
T': aaa
1: a (u, l) = (1, 1)
2: aa (u, l) = (2, 2), l>=n
3: aa (u, l) = (2, 2), l>=n
小Hi:T'[2]和T'[3]结尾的最长公共子串都是"aa",(u, l)都是(2, 2)。我们要避免"aa"的出现次数被统计2次,小Ho你想想要怎么办?
小Ho:恩,我们要记录一个状态是不是之前在l>=n的情况下到达过。如果到达过的话,下一次再到达就不要统计了。
小Hi:很好。我们还有第二个特殊情况要处理。那就是要区分串T'[i-l+1 .. i]出现次数和T'[i-n+1 .. i]的出现次数。前面说到,我们处理T'[i]的时候求出当前状态u和匹配长度l。这时串T'[i-l+1 .. i]一定是属于状态u的,T'[i-l+1 .. i]的出现次数是|endpos(u)|。但是这时可能l>n,所以T'[i-n+1 .. i]不一定属于状态u。T'[i-n+1 .. i]是T'[i-l+1 .. i]长度为n的后缀,可能在suffix-path(u->S)上,出现次数比T'[i-n+1 .. i]多。
小Ho:这个也好办,我们只要沿着suffix-path(u->S)向上找,找到最靠近S的v满足maxlen[v]>=n (也就是minlen[v]<=n<=maxlen[v]),统计|endpos(v)|即可。
小Hi:这里有一个关键点,我们找到v之后可以直接令u=v。以免每次向前找v的复杂度过高。
此题感悟:把trans当成KMP的fail函数,从而后缀自动机可以实现KMP和ac自动机的大部分功能。
注意题中的S=1;
此外字符串处理后再用strlen会出错?好像是。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
const int N=1e6+;
int q[N*],tail,head;
int tot,slink[*N],trans[*N][],minlen[*N],maxlen[*N],edpts[*N];
int blue[*N],ind[*N],used[*N];
char str[*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=; i<; 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]+,-,NULL,);
blue[z]=;//绿色
while(v&&!trans[v][c]) {
trans[v][c]=z;
v=slink[v];
}
if(!v) {
minlen[z]=;
slink[z]=;
ind[]++;
return z;
}
int x=trans[v][c];
if(maxlen[v]+==maxlen[x]) {
slink[z]=x;
minlen[z]=maxlen[x]+;
ind[x]++;
return z;
}
int y=newstate(maxlen[v]+,-,trans[x],slink[x]);
slink[z]=slink[x]=y;
ind[y]+=;
minlen[x]=minlen[z]=maxlen[y]+;
while(v&&trans[v][c]==x) {
trans[v][c]=y;
v=slink[v];
}
minlen[y]=maxlen[slink[y]]+;
return z;
}
void top_sort() {
head=tail=;
for(int i=;i<=tot;i++)if(!ind[i]) q[++tail]=i;
while(head<tail) {
int u=q[++head];
if(blue[u]) edpts[u]++;
edpts[slink[u]] += edpts[u];
if(!--ind[slink[u]]) q[++tail]=slink[u];
}
}
void _count()
{
char c[*N];
scanf("%s",c);
int len,L0,i,u=,ans=,L=;//
L0=strlen(c);
for(i=;i<L0-;i++) c[i+L0]=c[i];
len=*L0-;//改成strlen就错了!!!
for(i=;i<=tot;i++) used[i]=;
for(i=;i<len;i++){
while(u!=&&trans[u][c[i]-'a']==) {
u=slink[u];
L=maxlen[u];
}
if(trans[u][c[i]-'a']>) {
u=trans[u][c[i]-'a'];
L++;
}
else {
u=;
L=;
}//
if(L>L0){
while(maxlen[slink[u]]>=L0){
u=slink[u];
L=maxlen[u];
}
}
if(L>=L0&&!used[u]) {
ans+=edpts[u];
used[u]=;
}
}
printf("%d\n",ans);
}
int main() {
scanf("%s",str);
int len=strlen(str),pre=;
tot=;
for(int i=; i<len; i++) {
pre=add_char(str[i],pre);
}
top_sort();
int T;
scanf("%d",&T);
while(T--) _count();
return ;
}
HihoCoder1465 重复旋律8(后缀自动机)的更多相关文章
- HihoCoder1449 重复旋律6(后缀自动机)
描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi想知道一部作品中所有长度为K的旋律中出现次数最多的旋律的出现次数.但是K不是固定的,小Hi想知道对 ...
- hihoCoder.1465.后缀自动机五 重复旋律8(后缀自动机)
题目链接 \(Description\) 给定母串S,求模式串的循环同构串在S中的出现次数. \(Solution\) 将模式串s复制一遍,在母串的SAM上匹配,记录以每个位置作为后缀所能匹配的最大长 ...
- hihoCoder 1403 后缀数组一·重复旋律(后缀数组+单调队列)
#1403 : 后缀数组一·重复旋律 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构成 ...
- 【HIHOCODER 1403】后缀数组一·重复旋律(后缀数组)
描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构成的数列. 小Hi在练习过很多曲子以后发现很多作品自身包含一样的旋律.旋律是一段连续的数列,相似的旋律在原数列 ...
- HihoCoder 重复旋律
あの旋律を何度も繰り返しでも.あの日見た光景を再現できない 无论将那段旋律重复多少次,也无法重现那一日我们看到的景象 もし切ないならば.時をまきもどしてみるかい? 若是感到惆怅的话,要试着让时光倒流吗 ...
- HIHOcoder1465 后缀自动机五·重复旋律8
思路 后缀自动机求最长循环串 首先有一个常用的处理技巧,将串复制一遍,长度大于n的子串中就包含了一组循环子串 然后是后缀自动机如何处理最长公共子串的问题 维护两个变量,u和l,u代表当前位置的最长公共 ...
- BZOJ 后缀自动机四·重复旋律7
后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的 ...
- hihoCoder 后缀自动机三·重复旋律6
后缀自动机三·重复旋律6 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi ...
- hihoCoder #1445 : 后缀自动机二·重复旋律5
#1445 : 后缀自动机二·重复旋律5 时间限制:10000ms 单点时限:2000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数 ...
随机推荐
- POJ - 2464 Brownie Points II 【树状数组 + 离散化】【好题】
题目链接 http://poj.org/problem?id=2464 题意 在一个二维坐标系上 给出一些点 Stan 先画一条过一点的水平线 Odd 再画一条 过Stan那条水平线上的任一点的垂直线 ...
- 每天一个Linux命令(53)service命令
service命令用于对系统服务进行管理. (1)用法: 用法: service [服务] [操作] (2)功能: 功能: service命令用于启动.停止.重 ...
- 每天一个Linux命令(39)free命令
free命令可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区. (1)用法: 用法: free [选项参数] (2)功能: ...
- xml转换为对象 微信接口
public sealed class XMLSerilizable { /// <summary> /// XML转换为对象 /// </summary> /// <t ...
- 【HackerRank】Running Time of Quicksort
题目链接:Running Time of Quicksort Challenge In practice, how much faster is Quicksort (in-place) than I ...
- python中完善decorator
@decorator可以动态实现函数功能的增加,但是,经过@decorator“改造”后的函数,和原函数相比,除了功能多一点外,有没有其它不同的地方? 在没有decorator的情况下,打印函数名: ...
- 查看oracle当前连接数和进程数
查询数据库当前进程的连接数: select count(*) from v$process; 查看数据库当前会话的连接数: select count(*) from v$session; 查看数据库的 ...
- 进程控制块PCB结构体 task_struct 描述
进程控制块,英文名(Processing Control Block),简称 PCB . 进程控制块是系统为了管理进程设置的一个专门的数据结构,主要表示进程状态. 每一个进程都对应一个PCB来维护进程 ...
- CF697E && CF696C PLEASE
题意:给你三个杯子,一开始钥匙放在中间的杯子里,然后每一回合等概率将左右两个杯子中的一个与中间杯子交换.求n回合之后钥匙在中间杯子的概率.这里要求概率以分数形式输出,先化成最简,然后对1e9 + 7取 ...
- [转载]解析 Java 类和对象的初始化过程
原文地址:http://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/index.html 由一个单态模式引出的问题谈起 类的初始化和对象初始化 ...