http://acm.hust.edu.cn/vjudge/problem/28005

题意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。求F(1)..F(Length(S)) 。

题解:

关键问题在于统计某个串出现了多少次。

在后缀自动机中,答案即为包含了这个串的状态的right集合的大小。 
后缀自动机有两张DAG,一张是trans图,一张是parent树 
从trans图的角度出发,right集合的大小为该状态走到结束状态的方案数 
从parent树的角度出发,parent树是反串的后缀树,right集合的大小为该状态的子树中有多少个结点代表了反串的一个后缀(也就是原串的前缀) 
我们采取第二种计数方法,先将包含原串前缀的状态的right集合设为1(相当于在反串的后缀树中将后缀结点标记为1) 
因为parent树中我们并没有把儿子记下来,所以没办法直接对parent树bfs,但我们知道儿子的len严格大于父亲的len,所以我们按len的长度进行排序,然后按len从大到小,用当前点更新parent的答案 
我们发现一个结点代表的长度是一个区间,我们先只考虑该结点代表的最长长度,然后我们再用长串去更新短串(因为一个长串出现k次,它的所有后缀都至少出现k次)即可

————引用自http://blog.csdn.net/hbhcy98/article/details/51055733

首先要求节点x表示的最长的子串,也就是长度为step[x]的这个串出现了多少次——该节点的right集合。

right集合到底怎么求?

从parent树的角度出发,parent树是反串的后缀树,right集合的大小为该状态的子树中有多少个结点代表了反串的一个后缀(也就是原串的前缀)

在建好的自动机上跑一遍原串,经过的节点r[x]=1;

然后找出自动机的拓扑序,按着拓扑序的逆序for一遍,更新pre[x]。

一个节点贡献的子串长度区间是[min[x],max[x]],我们开始只考虑了该节点代表的最大长度max[x],也就是长度为step[x]这个子串。

然后我们用长串去更新短串。

因为sam是在线的,从root开始跳到x节点的路径必定是主链上root到x这条最长串(原串的前缀)的后缀。

一个长串出现k次,它的所有后缀都至少出现k次。

f[i-1]=maxx(f[i-1],f[i]);

机智啊。。。。。。我看了好几个题解才看懂。。。。TAT。。。

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
#include<ctime>
using namespace std; const int N=*;
char s[N];
int tot,last,sl,cl;
int son[N][],pre[N],step[N],in[N],c[N],r[N],f[N];
bool vis[N];
queue<int> Q; int maxx(int x,int y){return x>y ? x:y;} int add_node(int x){step[++tot]=x;/*r[tot]=1;*/return tot;} void clear()
{
memset(r,,sizeof(r));
memset(son,,sizeof(son));
memset(pre,,sizeof(pre));
memset(step,,sizeof(step));
tot=;add_node();last=;
} void extend(int ch)
{
int p=last,np=add_node(step[p]+);
while(p && !son[p][ch]) son[p][ch]=np,in[np]++,p=pre[p];
if(!p) pre[np]=;
else
{
int q=son[p][ch];
if(step[q]==step[p]+) pre[np]=q;
else
{
int nq=add_node(step[p]+);
memcpy(son[nq],son[q],sizeof(son[q]));
for(int i=;i<=;i++)
if(son[q][i]) in[son[q][i]]++;
pre[nq]=pre[q];
pre[np]=pre[q]=nq;
while(son[p][ch]==q) son[p][ch]=nq,in[nq]++,in[q]--,p=pre[p];
}
}
last=np;
} void find_tp()
{
while(!Q.empty()) Q.pop();
memset(vis,,sizeof(vis));
Q.push();vis[]=;cl=;
while(!Q.empty())
{
int x=Q.front();vis[x]=;c[++cl]=x;Q.pop();
for(int i=;i<=;i++)
{
int y=son[x][i];
if(!y) continue;
in[y]--;
if(!in[y] && !vis[y]) vis[y]=,Q.push(y);
}
}
} int main()
{
freopen("a.in","r",stdin);
scanf("%s",s+);
sl=strlen(s+);
clear();
for(int i=;i<=sl;i++) extend(s[i]-'a'+);
int x=,ch;
for(int i=;i<=sl;i++)
{
ch=s[i]-'a'+;
x=son[x][ch];
r[x]++;
}
find_tp();
for(int i=cl;i>=;i--)
{
x=c[i];
r[pre[x]]+=r[x];
// printf("r %d = %d step = %d\n",x,r[x],step[x]);
f[step[x]]=maxx(f[step[x]],r[x]);
}
// for(int i=1;i<=sl;i++) printf("%d ",f[i]);printf("\n");
for(int i=sl;i>=;i--) f[i-]=maxx(f[i],f[i-]);
for(int i=;i<=sl;i++) printf("%d\n",f[i]);
return ;
}

【spoj8222-Substrings】sam求子串出现次数的更多相关文章

  1. HDU 1686 Oulipo【kmp求子串出现的次数】

    The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e ...

  2. str2int HDU - 4436 后缀自动机求子串信息

    题意: 给出 n 个串,求出这 n 个串所有子串代表的数字的和. 题解; 首先可以把这些串构建后缀自动机(sam.last=1就好了), 因为后缀自动机上从 root走到的任意节点都是一个子串,所有可 ...

  3. H - Repeats (重复最多子串的次数)

    题目链接:https://cn.vjudge.net/contest/283743#problem/H 题目大意:T组数据,给你一个字符串,然后让你求这个字符串的重复最多子串的次数. 具体思路:论文题 ...

  4. hiho#1445 重复旋律5 求子串数量 后缀自动机

    题目传送门 题意:给出一个字符串,求子串的个数. 思路:后缀自动机的题真是每做一题就更理解一些. SAM中的每一状态$p$都代表了一种子串,而p包含的字符串的个数是$len[p]-len[fa[p]] ...

  5. poj 3461 Oulipo(kmp统计子串出现次数)

    题意:统计子串出现在主串中的次数 思路:典型kmp #include<iostream> #include<stdio.h> #include<string.h> ...

  6. 求子串-KPM模式匹配-NFA/DFA

    求子串 数据结构中对串的5种最小操作子集:串赋值,串比较,求串长,串连接,求子串,其他操作均可在该子集上实现 数据结构中串的模式匹配 KPM模式匹配算法 基本的模式匹配算法 //求字串subStrin ...

  7. 串的模式匹配算法(求子串位置的定位函数Index(S,T,pos))

    串的模式匹配的一般方法如算法4.5(在bo4-1.cpp 中)所示:由主串S 的第pos 个字 符起,检验是否存在子串T.首先令i 等于 pos(i 为S 中当前待比较字符的位序),j 等于 1(j ...

  8. [SPOJ8222]Substrings

    [SPOJ8222]Substrings 试题描述 You are given a string S which consists of 250000 lowercase latin letters ...

  9. hihocoder-1419 后缀数组四·重复旋律4 求连续重复次数最多的子串

    对于重复次数,如果确定了重复子串的长度len,那重复次数k=lcp(start,start+len)/len+1.而暴力枚举start和len的复杂度是O(n^2),不能接受.而有一个规律,若我们只枚 ...

随机推荐

  1. javascript数据相关处理,序列化反序列化,数据编码与解码

    对象序列化简而言之,将对象转为字符串.在数据的传输过程中,经常会使用到对象序列化. javascript中常用的对象序列化:JSON.stringify(); javascript中常用的对象反序列化 ...

  2. Kotlin 1 函数

    #2 函数 函数声明和平时我见到的有点不太一样,使用关键字fun来声明.(感觉好欢乐的样子···O(∩_∩)O~~) 下面的示例,简单的声明了一个函数: // 这是函数声明 fun this_is_a ...

  3. TortoiseGit小乌龟 git管理工具

    1.新建分支git远端新建分支: b001本地git目录:右击--TortoiseGit--获取(会获取到新建分支) 2.本地新建分支对应远端分支本地新建分支:b001 关联远端分支b001(之后工作 ...

  4. ADVICE FOR SHORT-TERM MACHINE LEARNING RESEARCH PROJECTS(短期机器学习研究的建议)

    – Tim Rocktäschel, Jakob Foerster and Greg Farquhar, 29/08/2018 Every year we get contacted by stude ...

  5. AMF3 在Unity中使用AMF3和Java服务器通信

    现在在做的项目是一个网页游戏的移植到移动端. 所以服务器直接使用原来的代码.原来的游戏是as3实现,使用flash amf3数据通信. Unity 使用C#作为脚本语言,所以就需要.net的amf3解 ...

  6. lintcode-110-最小路径和

    110-最小路径和 给定一个只含非负整数的m*n网格,找到一条从左上角到右下角的可以使数字和最小的路径. 注意事项 你在同一时间只能向下或者向右移动一步 样例 标签 动态规划 思路 使用动态规划,用二 ...

  7. Jboss6内存修改

    1.启动脚本:/home/jboss/jboss-eap-6.2/bin/standalone.sh -Djboss.bind.address.management=192.168.0.62 -Djb ...

  8. lnmp1.4,400,500,错误

    Thinkphp5或其他主流框架,入口文件未放在根目录下,比如Thinkphp5 入口文件放在/public/index.php vhost需要指向/public目录 一键安装包通常会报 open_b ...

  9. ThreadLocal 验明正身

    一.前言 之前ThreadLocal使用不多,有个细节也就注意不到了:ThreadLocal在多线程中到底起什么作用?用它保存的变量在每个线程中,是每个线程都保存一份变量的拷贝吗?带着这些问题,我查了 ...

  10. CHM格式的电子书打开是空白的解决办法

    CHM是英语“Compiled Help Manual”的简写,即“已编译的帮助文件”.CHM是微软新一代的帮助文件格式,利用HTML作源文,把帮助内容以类似数据库的形式编译储存.