【spoj8222】 Substrings

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}$,并且这个状态的Right集合可以直接加入当前这个Right。情况三是虽然存在状态${V_q}$,但是这个状态所包含的子串有一部分长度比较长的无法包含要加入的这个Right,所以将它拆成两份。

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

  代码模着hzwer写的,加了点注释。

代码

// 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=500010;
int n,last,Dargen,cnt;
int ch[maxn][26],a[maxn],b[maxn],t[maxn],f[maxn],l[maxn],r[maxn],fa[maxn];
char s[maxn];

void add(int x) {
	int c=a[x];
	int p=last,np=++cnt;last=np;   //p是上次插入的节点,np为现在正在插入的这个节点,last变成了np
	l[np]=x;   //Max(s),也就是到达这个节点的最长的子串(相当于x的前缀的长度→_→)
	for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;   //p以及其在parent树上的祖先没有c边的全部连边(可以从right集合的角度去考虑)
	if (!p) fa[np]=Dargen;   //如果都没有c边,则np的parent为dargen————情况1
	else {
		int q=ch[p][c];   //找到了深度最深的有c边的祖先
		if (l[q]==l[p]+1) fa[np]=q;   //情况2
		else {   //情况3
			int nq=++cnt;l[nq]=l[p]+1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			fa[nq]=fa[q];
			fa[np]=fa[q]=nq;
			for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
		}
	}
}
int main() {
	scanf("%s",s+1);
	int len=strlen(s+1);
	for (int i=1;i<=len;i++) a[i]=s[i]-‘a‘;
	last=Dargen=++cnt;
	for (int i=1;i<=len;i++) add(i);
	for (int p=Dargen,i=1;i<=len;i++) p=ch[p][a[i]],r[p]++;   //先将主链上的right全部加1
	for (int i=1;i<=cnt;i++) b[l[i]]++;   //按照l[x]从小到大基数排序
	for (int i=1;i<=len;i++) b[i]+=b[i-1];
	for (int i=1;i<=cnt;i++) t[b[l[i]]--]=i;
	for (int i=cnt;i>=1;i--) r[fa[t[i]]]+=r[t[i]];   //从后往前for,更新parent的right大小
	for (int i=1;i<=cnt;i++) f[l[i]]=max(f[l[i]],r[i]);   //更新答案
	for (int i=len;i>=1;i--) f[i]=max(f[i],f[i+1]);
	for (int i=1;i<=len;i++) printf("%d\n",f[i]);
    return 0;
}
时间: 2024-10-01 06:36:08

【spoj8222】 Substrings的相关文章

【spoj8222】Substrings

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 500005 7 #define maxm 250005 8 using namespace std; 9 10 int n,tot,root,last,f[maxm],fa[maxn],son[maxn][2

【SPOJ8222】Substrings (后缀自动机)

题意: 给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值. 求F(1)..F(Length(S)) Length(S) <= 250000 思路:板子中st[x]定义为root到x的最多步数,可以用来更新所有长度为[1..st[x]]的答案 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned int uint; 5 typedef u

【SPOJ】Substrings(后缀自动机)

/* 求right集合大小 然后后缀最大值 */ #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<queue> #define ll long long #define M 600010 #define mmp make_pair using namespace std; int read() { int nm = 0, f =

【CF316G3】Good Substrings 后缀自动机

[CF316G3]Good Substrings 题意:给出n个限制(p,l,r),我们称一个字符串满足一个限制当且仅当这个字符串在p中的出现次数在[l,r]之间.现在想问你S的所有本质不同的子串中,有多少个满足所有限制. |S|,|p|<=10^5,n<=10. 题解:比较简单的后缀自动机题,我们先把原串和所有限制串放到一起建一个广义后缀自动机,然后在pre树上统计一下即可得到每个子串在每个限制串中出现了多少次.现在我们想知道原串中有多少满足条件的子串,即我们统计一下所有出现次数符合要求的,

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

[SPOJ]Distinct Substrings(后缀自动机) 题面 Vjudge 题意:求一个串的不同子串的数量 题解 对于这个串构建后缀自动机之后 我们知道每个串出现的次数就是\(right/endpos\)集合的大小 但是实际上我们没有任何必要减去不合法的数量 我们只需要累加每个节点表示的合法子串的数量即可 这个值等于\(longest-shortest+1=longest-parent.longest\) #include<iostream> #include<cstdio&g

LeetCode | 1358. Number of Substrings Containing All Three Characters包含所有三种字符的子字符串数目【Python】

LeetCode 1358. Number of Substrings Containing All Three Characters包含所有三种字符的子字符串数目[Medium][Python][双指针][滑窗] Problem LeetCode Given a string s consisting only of characters a, b and c. Return the number of substrings containing at least one occurrence

【spoj705】 Distinct Substrings

[题目描述] 给定一个字符串,计算其不同的子串个数. [输入格式] 一行一个仅包含大写字母的字符串,长度<=50000 [输出格式] 一行一个正整数,即不同的子串个数. [样例输入] ABABA [样例输出] 9 [思路] 一看就知道是后缀数组题啦-但是我不会写QAQ..只好现学现用啦- 在字符串最后补上一个'$',不因为别的只因为它比‘A’还要小..不然你补ascII码是0的也可以.. 申请rank数组和sa数组,rank[i]=j代表后缀i排第j位,sa[i]=j代表排名第i的是后缀j.也就

【转】对于杭电OJ题目的分类

[好像博客园不能直接转载,所以我复制过来了..] 1001 整数求和 水题1002 C语言实验题——两个数比较 水题1003 1.2.3.4.5... 简单题1004 渊子赛马 排序+贪心的方法归并1005 Hero In Maze 广度搜索1006 Redraiment猜想 数论:容斥定理1007 童年生活二三事 递推题1008 University 简单hash1009 目标柏林 简单模拟题1010 Rails 模拟题(堆栈)1011 Box of Bricks 简单题1012 IMMEDI

【BZOJ2780】[Spoj]8093 Sevenk Love Oimaster 广义后缀自动机

[BZOJ2780][Spoj]8093 Sevenk Love Oimaster Description Oimaster and sevenk love each other.     But recently,sevenk heard that a girl named ChuYuXun was dating with oimaster.As a woman's nature, sevenk felt angry and began to check oimaster's online t