SPOJ8222 Substrings( 后缀自动机 + dp )

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

建出SAM, 然后求出Right, 求Right可以按拓扑序dp..Right就是某个点到结束状态的路径数, parent树上last的那一条链都是结束状态...然后用Right去更新答案..

spoj卡常数..一开始用DFS就炸了, 改用BFS就A了..

(贴一下丽洁姐的题解: 我们构造S的SAM,那么对于一个节点s,它的长度范围是[Min(s),Max(s)],同时他的出现次数是|Right(s)|。那么我们用|Right(s)|去更新F(Max(s))的值。同时最后从大到小依次用F(i)去更新F(i-1)即可。)

-----------------------------------------------------------------------------

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<queue>

using namespace std;

const int cn = 26;

const int maxn = 1000009;

bool vis[maxn];

int Right[maxn], ans[maxn], deg[maxn], n = 0, N;

char s[maxn];

struct Node {

Node *ch[cn], *fa;

int len, id;

} pool[maxn], *pt, *root, *last;

Node* newNode(int v) {

memset(pt->ch, 0, sizeof pt->ch);

pt->fa = 0;

pt->len = v;

pt->id = n++;

return pt++;

}

void SAM_init() {

pt = pool;

root = last = newNode(0);

}

void Extend(int c) {

Node *p = last, *np = newNode(p->len + 1);

for(; p && !p->ch[c]; p = p->fa)

p->ch[c] = np;

if(!p)

np->fa = root;

else {

Node* q = p->ch[c];

if(p->len + 1 == q->len)

np->fa = q;

else {

Node* nq = newNode(p->len + 1);

memcpy(nq->ch, q->ch, sizeof q->ch);

nq->fa = q->fa;

q->fa = np->fa = nq;

for(; p && p->ch[c] == q; p = p->fa)

p->ch[c] = nq;

}

}

last = np;

}

struct edge {

int to;

edge* next;

} E[maxn], *Pt = E, *head[maxn];

void AddEdge(int u, int v) {

deg[Pt->to = v]++; Pt->next = head[u]; head[u] = Pt++;

}

void SAM_build() {

scanf("%s", s);

N = strlen(s);

for(int i = 0; i < N; i++)

Extend(s[i] - ‘a‘);

}

queue<Node*> q;

queue<int> Q;

void ADDEDGE() {

memset(deg, 0, sizeof deg);

memset(vis, 0, sizeof vis);

q.push(root);

vis[root->id] = true;

while(!q.empty()) {

Node* t = q.front(); q.pop();

for(int i = 0; i < cn; i++) if(t->ch[i]) {

AddEdge(t->ch[i]->id, t->id);

if(!vis[t->ch[i]->id]) {

q.push(t->ch[i]);

vis[t->ch[i]->id] = true;

}

}

}

}

void getRight() {

memset(Right, 0, sizeof Right);

for(Node* t = last; t; t = t->fa)

Right[t->id] = 1;

Q.push(last->id);

while(!Q.empty()) {

int x = Q.front(); Q.pop();

for(edge* e = head[x]; e; e = e->next) {

Right[e->to] += Right[x];

if(!--deg[e->to])

Q.push(e->to);

}

}

}

void update() {

memset(vis, 0, sizeof vis);

q.push(root);

vis[root->id] = true;

while(!q.empty()) {

Node* t = q.front(); q.pop();

ans[t->len] = max(ans[t->len], Right[t->id]);

for(int i = 0; i < cn; i++) if(t->ch[i] && !vis[t->ch[i]->id]) {

q.push(t->ch[i]);

vis[t->ch[i]->id] = true;

}

}

}

void solve() {

getRight();

update();

for(int i = N; --i; )

ans[i] = max(ans[i], ans[i + 1]);

for(int i = 1; i <= N; i++)

printf("%d\n", ans[i]);

}

int main() {

SAM_init();

SAM_build();

ADDEDGE();

solve();

return 0;

}

-----------------------------------------------------------------------------

时间: 2024-10-12 15:08:20

SPOJ8222 Substrings( 后缀自动机 + dp )的相关文章

【bzoj3998】[TJOI2015]弦论 后缀自动机+dp

题目描述 对于一个给定长度为N的字符串,求它的第K小子串是什么. 输入 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. 输出 输出仅一行,为一个数字串,为第K小的子串.如果子串数目不足K个,则输出-1 样例输入 aabc 0 3 样例输出 aab 题解 后缀自动机+dp 先对原串建立后缀自动机,然后在其上面跑dp统计每个节点开始的串的个数. 设f[i]表示与位置i有相同前缀的串

【POJ3415】Common Substrings 后缀自动机

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42710069 其实我就是觉得原创的访问量比未授权盗版多有点不爽233... 题意: 给两个串,问有多少长度大于等于K的公共子串(位置不同也算一对) 题解: 后缀自动机DP 对第一个串建立后缀自动机,然后做一些预处理, 然后拿第二个串在后缀自动机上跑,到每个节点加一次贡献. 但是这样需要每个点往parent树上跑一遍,会TLE,所以可以加个lazy. 然后代码中有两次运用到拓扑序来从子向父

【SPOJ -NSUBSTR】Substrings 【后缀自动机+dp】

题意 给出一个字符串,要你找出所有长度的子串分别的最多出现次数. 分析 我们建出后缀自动机,然后预处理出每个状态的cnt,cnt[u]指的是u这个状态的right集合大小.我们设f[len]为长度为len的子串的最多出现次数.我们对于自动机的每个状态都更新f,f[st[u].len]=max(f[st[u].len],cnt[u]).然后这样更新完以后,可以神奇的dp一下.f[len]=max(f[len],f[len+1]).想想为什么? 1 #include <cstdio> 2 #inc

bzoj 2806: [Ctsc2012]Cheat 后缀自动机DP

2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 583  Solved: 330[Submit][Status][Discuss] Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库 的行数 接下来M行的01串,表示标准作文库 接下来N行的01串,表示N篇作文 Output N行,每行一个整数,表示这篇作文的Lo 值. Sample Input 1 2 101

【CF316G3】Good Substrings 后缀自动机

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

SPOJ8222 NSUBSTR - Substrings 后缀自动机_动态规划

讲起来不是特别好讲.总之,如果 $dp[i+1]>=dp[i]$,故$dp[i]=max(dp[i],dp[i+1])$ Code: #include <cstdio> #include <algorithm> #include <cstring> #define setIO(s) freopen(s".in","r",stdin) #define maxn 2000000 #define N 30 #define ll l

[SPOJ8222]NSUBSTR - Substrings 后缀自动机

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int sz=0,la,rt; 6 int ch[500010][26],l[500010],fa[500010]; 7 void Extend(int c){ 8 int end=++sz,tmp=la; 9 l[end]=l[tmp]+1; 10 while(tmp&&!ch[tm

2019HNCPC C Distinct Substrings 后缀自动机

题意 给定一个长度为n字符串,字符集大小为m(1<=n,m<=1e6),求\(\bigoplus_{c = 1}^{m}\left(h(c) \cdot 3^c \bmod (10^9+7)\right)\)的值.其中h(c)为将c加到字符串末尾产生的新的本质不同的子串数目. 解题思路 比赛的时候没做出来,颁奖的时候听lts和lsx讲了之后发现可以用SAM做,而且板子稍微改改就可以了. 具体就是每次添加一个字符最多新建2个节点,根据SAM的性质,添加c后新建节点对本质不同的子串的数目的贡献就是

BZOJ 2806 [Ctsc2012]Cheat ——后缀自动机 单调队列优化DP

先建出广义后缀自动机. 然后跑出文章中每一个位置的最大匹配距离. 然后定义$f[i]$表示匹配到以$i$结尾的串时,最长的匹配距离. 显然可以二分$L$的取值. 然后容易得到$DP$方程 $f[i]=max(f[i-1],f[j]+i-j)(j<=i-L)$ 然后就发现$j$属于一个区间,然后就可以单调队列优化了. #include <map> #include <ctime> #include <cmath> #include <queue> #in