并不对劲的[spoj nsubstr]substrings

题意是求一个字符串每个长度的子串出现次数最多的那个出现了多少次,也就是求每个到根的最长路的right集合最大值 。

先建后缀自动机,然后将每个前缀所在的集合的初值设为1,因为所有前缀的right集合肯定不相同,而且它们包含了所有位置。

接下来按到根的最长距离从大到小排序,将right集合累加到parent上。这么排序是因为到根的最长距离长的状态肯定不是到根的最长距离短的状态的parent。

最后直接求到根的不同的最长距离的最大的right集合就行。

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define maxn 250010
using namespace std;
int ans,len,p;
int read()
{
    int f=1,x=0;char ch=getchar();
    while(isdigit(ch)==0 && ch!=‘-‘)ch=getchar();
    if(ch==‘-‘)f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar();
    return x*f;
}
void write(int x)
{
    int ff=0;char ch[15];
    while(x)ch[++ff]=(x%10)+‘0‘,x/=10;
    if(ff==0)putchar(‘0‘);
    while(ff)putchar(ch[ff--]);
    putchar(‘\n‘);
}
typedef struct node
{
    int to[30],dis,fa;
}spot;
//求每个dis的right集合最大值
//不是每个状态的right集合!!
//ass♂we♂can♂
struct SAM
{
    spot x[maxn<<1];
    int ls,ls2,q,cnt,rt,lst,c[maxn<<1],ord[maxn<<1],ans[maxn<<1],maxl[maxn<<1];
    int right[maxn<<1],f[maxn<<1];
    char s[maxn];
    inline void start()
    {
        lst=rt=++cnt;
        scanf("%s",s+1);
        ls=strlen(s+1);
        for(int i=1;i<=ls;i++)
            extend(i);
    }
    inline void extend(int pos)
    {
        int val=s[pos]-‘a‘,p=lst,np=++cnt;
        lst=np,x[np].dis=pos;
        for(;p&&x[p].to[val]==0;p=x[p].fa)x[p].to[val]=np;
        if(p==0)x[np].fa=rt;
        else
        {
            int q=x[p].to[val];
            if(x[q].dis==x[p].dis+1)x[np].fa=q;
            else
            {
                int nq=++cnt;
                x[nq].dis=x[p].dis+1;
                memcpy(x[nq].to,x[q].to,sizeof(x[q].to));
                x[nq].fa=x[q].fa,x[np].fa=x[q].fa=nq;
                for(;x[p].to[val]==q;p=x[p].fa)x[p].to[val]=nq;
            }
        }
    }
    inline void qsort()
    {
        for(int i=1;i<=cnt;i++)
            c[x[i].dis]++;
        for(int i=1;i<=ls;i++)
            c[i]+=c[i-1];
        for(int i=1;i<=cnt;i++)
            ord[c[x[i].dis]--]=i;
    }
    inline void work()
    {
        for(int i=1,p=rt;i<=ls;i++)
            p=x[p].to[s[i]-‘a‘],right[p]=1;
        for(int i=cnt;i>=1;i--){int u=ord[i];right[x[u].fa]+=right[u];}
        for(int i=1;i<=cnt;i++)f[x[i].dis]=max(f[x[i].dis],right[i]);
        for(int i=ls;i>=1;i--)f[i]=max(f[i],f[i+1]);
        for(int i=1;i<=ls;i++)write(f[i]);
    //    cout<<"He doesn‘t have hands->"<<endl;
    }
}t;
int main()
{
    t.start();
    t.qsort();
    t.work();
    return 0;
}

并不对劲的SAM

听某位大佬说后缀自动机能解决所有不是字符串dp的字符串题。

原文地址:https://www.cnblogs.com/xzyf/p/8318236.html

时间: 2024-08-04 12:59:28

并不对劲的[spoj nsubstr]substrings的相关文章

SPOJ NSUBSTR - Substrings

NSUBSTR - Substrings no tags You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as the maximal number of times that some string with length x appears in S. For example for string 'ababa' F(3) will be 2 b

SPOJ 8222 NSUBSTR Substrings

SAM的简单应用.... 由SAM可知从root到达的每个节点所经过的路径都对着应原串的一个子串,每个节点能到几次接收态就等于这个子串出现了几次.从最后一个节点往上走,就可以用DP更新出每个子串出现了多少次. 出现了5次的子串一定也出现了4,3,2,1次...所以最后再用长度长的给长度小的更新一下.... Substrings Time Limit: 1000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu [Submit]  

SPOJ - NSUBSTR 后缀自动机板子

SPOJ - NSUBSTR #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PII pair<int, int> #define PLI pair<LL, int> #define PDD pair<double,double> #define ull unsigned long long

●SPOJ 8222 NSUBSTR - Substrings

题链: http://www.spoj.com/problems/NSUBSTR/ 题解: 后缀自动机的水好深啊!懂不了相关证明,带着结论把这个题做了.看来这滩深水要以后再来了. 本题要用到一个叫 Right[P] 的数组,表示 P对应的子串在原串中出现的所有位置的末尾位置下标的集合.本题中,用这个数组存储集合大小就好了,即 P对应的子串在原串中出现了Right[p]次. 而Right[P]的值,等于从改点出发到结束状态的方案数.但这个不好求,而是要用到另一个求法:用 Parent树: (暂时由

【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

SPOJ 8222 Substrings(后缀自动机)

[题目链接] http://www.spoj.com/problems/NSUBSTR/ [题目大意] 给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值. 求出所有的F. [题解] 在SAM中,一个串出现的次数就是|Right(s)|,我们按长度从小到大分配内存单位, 从后往前计算可以获得Right值大小,用所有的Right去更新相应长度的答案即可. [代码] #include <cstdio> #include <cstring> #include <

SPOJ Distinct Substrings(后缀数组求不同子串个数,好题)

DISUBSTR - Distinct Substrings no tags Given a string, we need to find the total number of its distinct substrings. Input T- number of test cases. T<=20; Each test case consists of one string, whose length is <= 1000 Output For each test case output

SPOJ NSUBSTR

题目大意: 给定一个字符串,求每个对应的长度能产生的相同子串的最大个数 这里构建好后缀自动机之后,再将整个字符串从头到尾扫一遍,然后将每个对应的点上的sc值+1 表示从头走到尾的前提下,所能产生的子串能够得到的最大数量为1 然后再去考虑其他子串 每个后缀自动机上的节点上的长度表示的是当前点所能接收的最大长度的后缀 我们只考虑这个最大长度即可,因为其他没考虑的长度,最后都不断通过dp[i] = max(dp[i] , dp[i+1]) 得到即可 拓扑排序后,从尾节点开始不断往前,相当于由子节点不断

[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