http://www.spoj.com/problems/NSUBSTR/ (题目链接)

题意

  给出一个字符串S,令${F(x)}$表示S的所有长度为x的子串出现次数的最大值。求${F(1)......F(length(S))}$

Solution

  后缀自动机例题,下面写几点自己认为理解后缀自动机的重点。

  • 后缀自动机相对于后缀树就是将Right集合相同的子串合用一个节点来表示。每一个节点代表一个状态S,这个状态可能包含很多长度区间连续的子串,这些子串的右端点固定,它们的Right集合相同。
  • 往上跳parent的过程相当于将子串的前面一节截掉,得到一个长度更短的子串,它们的Right集合变多了。走状态转移边的过程相当于在子串的后面添加新的字符,到达新的状态。
  • 构造过程中,情况一很好理解,考虑情况二和情况三的区别。情况二是parent中存在x边到达某一个状态${V_q}$,并且这个状态的所有子串都可以接受当前插入的这个这个后缀。情况三是虽然存在状态${V_q}$,但是这个状态所包含一部分长度比较长的的子串无法接受要插入的这个后缀,所以将它拆成两份,一份可以接受,一份不能接受。

  对于这道题,我们需要做的就是计算SAM中每个节点的Right集合的大小,即在串中的出现次数。因为parent树的某个节点Right集合是它父亲的真子集,所以我们考虑从parent树的底端向上不断更新祖先的Right集合。

  代码模着hzwer写的,加了点注释。这里对Max的排序用了基数排序,看着好不爽,但是nlogn的算法就TLE了→_→。

  看了DaD3zZ的程序大有所感,update一下。

代码

// spoj8222
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std; const int maxn=250010;
int n;
int f[maxn];
char s[maxn]; namespace SAM {
int last,Dargen,sz;
int ch[maxn<<1][26],id[maxn<<1],len[maxn<<1],par[maxn<<1],r[maxn<<1],b[maxn];
void Extend(int c) {
int np=++sz,p=last;last=np; //p是上次插入的节点,np为现在正在插入的这个节点,last变成了np
len[np]=len[p]+1; //Max(s),也就是到达这个节点的最长的子串(相当于x的前缀的长度→_→)
for (;p && !ch[p][c];p=par[p]) ch[p][c]=np; //p以及其在parent树上的祖先没有c边的全部连边(可以从right集合的角度去考虑)
if (!p) par[np]=Dargen; //如果都没有c边,则np的parent为Dargen————情况1
else {
int q=ch[p][c]; //找到了深度最深的有c边的祖先
if (len[q]==len[p]+1) par[np]=q; //情况2
else { //情况3
int nq=++sz;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
par[nq]=par[q];
par[np]=par[q]=nq;
for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq;
}
}
}
void build() {
last=Dargen=sz=1;
for (int i=1;i<=n;i++) Extend(s[i]-'a');
}
void pre() {
for (int p=Dargen,i=1;i<=n;i++) p=ch[p][s[i]-'a'],r[p]++; //先将主链上的right全部加1
for (int i=1;i<=sz;i++) b[len[i]]++; //按照len[x]从小到大基数排序,相当于对SAM图进行拓扑排序
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=1;i<=sz;i++) id[b[len[i]]--]=i;
for (int i=sz;i>=1;i--) r[par[id[i]]]+=r[id[i]]; //从后往前for,自底向上更新parent的right大小
}
void solve() {
for (int i=1;i<=sz;i++) f[len[i]]=max(f[len[i]],r[i]); //更新答案
for (int i=n;i>=1;i--) f[i]=max(f[i],f[i+1]);
}
}
using namespace SAM; int main() {
scanf("%s",s+1);
n=strlen(s+1);
build();
pre();
solve();
for (int i=1;i<=n;i++) printf("%d\n",f[i]);
return 0;
}

【spoj8222】 Substrings的更多相关文章

  1. 【spoj8222】Substrings

    #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...

  2. 【SPOJ8222】Substrings (后缀自动机)

    题意: 给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值. 求F(1)..F(Length(S)) Length(S) <= 250000 思路:板子中st[x]定义为r ...

  3. 【SPOJ】Substrings(后缀自动机)

    [SPOJ]Substrings(后缀自动机) 题面 Vjudge 题意:给定一个长度为\(len\)的串,求出长度为1~len的子串中,出现最多的出现了多少次 题解 出现次数很好处理,就是\(rig ...

  4. 【SPOJ】Substrings

    出现次数很好处理,就是 \(right/endpos\) 集合的大小 那么,直接构建 \(SAM\) 求出每个位置的\(right\)集合大小 直接更新每个节点的\(longest\)就行了 最后短的 ...

  5. 【POJ1226】Substrings(后缀数组,二分)

    题意: n<=10,len<=100 思路: 只有一个字符串的时候特判一下 #include<cstdio> #include<cstring> #include& ...

  6. 【UVA10829】 L-Gap Substrings (后缀数组)

    Description If a string is in the form UVU, where U is not empty, and V has exactly L characters, we ...

  7. 【POJ3415】 Common Substrings(后缀数组|SAM)

    Common Substrings Description A substring of a string T is defined as: T(i, k)=TiTi+1...Ti+k-1, 1≤i≤ ...

  8. 【SPOJ】Distinct Substrings(后缀自动机)

    [SPOJ]Distinct Substrings(后缀自动机) 题面 Vjudge 题意:求一个串的不同子串的数量 题解 对于这个串构建后缀自动机之后 我们知道每个串出现的次数就是\(right/e ...

  9. 【SPOJ】Distinct Substrings/New Distinct Substrings(后缀数组)

    [SPOJ]Distinct Substrings/New Distinct Substrings(后缀数组) 题面 Vjudge1 Vjudge2 题解 要求的是串的不同的子串个数 两道一模一样的题 ...

随机推荐

  1. [转]Android 导入v7包常见错误,以及项目引用v7包错误解决

    android下v4    v7   v21等包是android系统的扩展支持包,就想windows的系统补丁一个道理. android的扩展包主要是用来兼容低版本的,比如android3.0以后出现 ...

  2. push以及pop,shift,unshift

    压入数组:往数组后面加:push         arr.push()返回值为添加后数组的长度 往数组前面加:unshift     arr.unshift()返回值为添加后数组的长度 拿出数组:拿掉 ...

  3. css 8.1

    1. border-collapse 属性设置是否将表格边框折叠为单一边框: table { border-collapse:collapse;  } 如果没有规定 !DOCTYPE,border-c ...

  4. ios学习基础篇一

    搜集的不错的oc学习资料 大概总结: http://my.oschina.net/luoguankun/blog/208526 详细教程: http://www.w3cschool.cc/ios/io ...

  5. Python -- 文档测试

    Python内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试. 例子: # mydict2.py class Dict(dict): ''' Simple dict but ...

  6. linux下如何修改iptables开启80端口

    linux下如何修改iptables开启80端口   最近在做本地服务器的环境,发现网站localhost能正常访问,用ip访问就访问不了,经常使用CentOS的朋友,可能会遇到和我一样的问题.开启了 ...

  7. Chapter 1 First Sight——14

    I parked in front of the first building, which had a small sign over the door reading front office. ...

  8. Junit单元测试的简单使用(主要是在spring框架下的项目)

    首先是解释什么是单元测试,单元测试是指对于一个大型项目里,对于单一模块或者单一接口的测试. 然后解释为什么要写单元测试,首先对于一个大型的项目,如果你每次都要重启一遍服务器调页面或者接口的bug,那就 ...

  9. WINCE下进程间通信(二)

    WINCE下进程间通信(二) 接着前面的文章<WINCE下进程间通信(一)>,现在介绍进程间通信的另一种方法. 三.管道(消息队列) WINCE并不支持类似于PC机上匿名管道.命名管道的通 ...

  10. Hadoop webHDFS设置和使用说明

    1.配置 namenode的hdfs-site.xml是必须将dfs.webhdfs.enabled属性设置为true,否则就不能使用webhdfs的LISTSTATUS.LISTFILESTATUS ...