• sss


    <更新提示>

    <第一次更新>


    <正文>

    Cool Slogans

    Description

    给定一个字符串 (S),要求构造字符串序列 (s_1,s_2,ldots,s_k),满足任意 (s_i) 都是 (S) 的子串,且任意 (iin[2,n]),都有 (s_i)(s_{i-1}) 中出现了至少 $2$ 次(可以有重叠部分,只要起始、结尾位置不同即可)。

    求可能的最大的 (k) 的值(即序列的最大可能长度)。

    Input Format

    第一行一个正整数$n$表示字符串长度。

    第二行一行一个字符串$S$。

    Output Format

    一行一个正整数表示最大的$k$值。

    Sample Input

    11
    abracadabra
    

    Sample Output

    3
    

    解析

    看到子串,考虑先建立后缀自动机。

    不难发现,答案一定可以表示为$mathrm(树上一条竖直的链,因为)mathrm$树上的父子关系就是后缀关系,从一个串中取子串肯定可以精简为取一个后缀。

    那就考虑在$mathrm$树上$dp$,$f_i$表示从根节点到节点$i$这条链的所有节点构成的序列的最长长度,显然是从祖先向当前节点转移。根据子串的性质,当然是越深的祖先转移过来越好,也就是能转移就转移即可。

    那么现在的问题就是如何判断是否能够转移了。假设要判定$fa$是否可以转移到$u$,那么在$u$代表字符串的每一个出现位置都应该出现了至少两次$fa$代表的字符串。因为$fa$代表的字符串是$u$的一个后缀,所以一定有一次出现在了$pos_(,那么只要判定在区间)[pos_u-mathrm+mathrm,pos_u-1]$内是否出现过$fa$代表的字符串就可以了。

    判定方法是用线段树合并求出每一个节点的具体$mathrm$集合,然后直接区间查询一下就可以了。

    但是我们想起之前学的线段树合并好像不能保留合并前的线段树,很容易想到一个方法就是可持久化一下,只需要把新的点开出来就可以保留旧的线段树了,时空复杂度仍然是对的,就是空间常数大了一点。

    (Code:)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 400020 , Maxlog = 25;
    struct SuffixAutomaton
    {
        int trans[N][26],link[N],maxlen[N],tot,last;
        int buc[N],ord[N],id[N];
        SuffixAutomaton () { last = tot = 1; }
        inline void Extend(int c,int pos)
        {
            int cur = ++tot , p;
            maxlen[cur] = maxlen[last] + 1 , id[cur] = pos;
            for ( p = last; p && !trans[p][c]; p = link[p] )
                trans[p][c] = cur;
            if ( p == 0 ) link[cur] = 1;
            else {
                int q = trans[p][c];
                if ( maxlen[q] == maxlen[p] + 1 ) link[cur] = q;
                else {
                    int cl = ++tot;
                    maxlen[cl] = maxlen[p] + 1 , id[cl] = id[q];
                    memcpy( trans[cl] , trans[q] , sizeof trans[q] );
                    while ( p && trans[p][c] == q )
                        trans[p][c] = cl , p = link[p];
                    link[cl] = link[q] , link[q] = link[cur] = cl;
                }
            }
            last = cur;
        }
        inline void Topsort(int n)
        {
            for (int i = 1; i <= tot; i++) ++buc[ maxlen[i] ];
            for (int i = 1; i <= n; i++) buc[i] += buc[i-1];
            for (int i = 1; i <= tot; i++) ord[ buc[maxlen[i]]-- ] = i;
        }
    };
    struct SegmentTree
    {
        struct node { int ls,rs; };
        node ver[N*Maxlog]; int tot;
        #define ls(p) ver[p].ls
        #define rs(p) ver[p].rs
        #define mid ( l + r >> 1 )
        inline void Insert(int &p,int l,int r,int v)
        {
            if ( !p ) p = ++tot; if ( l == r ) return void();
            if ( v <= mid ) Insert( ls(p) , l , mid , v );
            if ( v > mid ) Insert( rs(p) , mid+1 , r , v );
        }
        inline int Merge(int p,int q)
        {
            if ( !p || !q ) return p|q;
            int cur = ++tot;
            ls(cur) = Merge( ls(p) , ls(q) );
            rs(cur) = Merge( rs(p) , rs(q) );
            return cur;
        }
        inline int Query(int p,int l,int r,int ql,int qr)
        {
            if ( p == 0 ) return false; int res = 0;
            if ( ql <= l && r <= qr ) return true;
            if ( ql <= mid && ls(p) )
                res |= Query( ls(p) , l , mid , ql , qr );
            if ( qr > mid && rs(p) )
                res |= Query( rs(p) , mid+1 , r , ql , qr );
            return res;
        }
    };
    SuffixAutomaton T; SegmentTree S;
    char s[N]; int n,f[N],top[N],root[N],Ans(1);
    int main(void)
    {
        scanf( "%d" , &n );
        scanf( "%s" , s+1 );
        for (int i = 1; i <= n; i++)
            T.Extend( s[i] - 'a' , i ) , S.Insert( root[T.last] , 1 , n , i );
        T.Topsort(n);
        for (int i = T.tot; i >= 2; i--)
        {
            int u = T.ord[i] , Fa = T.link[u];
            root[Fa] = S.Merge( root[Fa] , root[u] );
        }
        for (int i = 2; i <= T.tot; i++)
        {
            int u = T.ord[i] , Fa = T.link[u];
            if ( Fa == 1 ) { f[u] = 1 , top[u] = u; continue; }
            int ql = T.id[u] - T.maxlen[u] + T.maxlen[top[Fa]] , qr = T.id[u] - 1;
            if ( S.Query( root[ top[Fa] ] , 1 , n , ql , qr ) )
                f[u] = f[ top[Fa] ] + 1 , top[u] = u;
            else f[u] = f[ top[Fa] ] , top[u] = top[Fa];
            Ans = max( Ans , f[u] );
        }
        printf( "%d
    " , Ans );
        return 0;
    }
    
    

    <后记>

  • 相关阅读:
    实验3-4 统计字符 (15分)
    实验3-5 查询水果价格 (15分)
    实验3-6 计算个人所得税 (10分)
    实验3-7 统计学生成绩 (15分)
    实验3-8 输出三角形面积和周长 (15分)
    实验3-9 三天打鱼两天晒网 (15分)
    实验3-11 计算油费 (15分)
    由一个商城项目得到的感悟
    ActiveMQ(三)——理解和掌握JMS(1)
    ActiveMQ(二)——ActiveMQ的安装和基本使用
  • 原文地址:https://www.cnblogs.com/Parsnip/p/12194219.html
Copyright © 2020-2023  润新知