2020 牛客多校4 C (后缀自动机)

题意

给你一个字符串,有一个操作f,任意取一个区间[l,r],获得一个字符串,这个字符串第i个字符为[l,l + i -1],区间内的最大的字符。求经过所有可能的操作之后,所有本质不同的字符串的种数。

比如样例1,dbca,
从d开始有d,dd,ddd,dddd,
从b开始有b,bc,bcc,
从c开始有,c,cc,
从a开始有a
总共10种。

看了大佬的题解,发现原来只要简单改一改后缀自动机的操作就好(T▽T)。他的思路是从后往前遍历字符串,然后根据题意我们知道从i开始的后缀相比i + 1的后缀,在遇到比第i个字符大或相等的字符之前都会改变原先的前缀,但之后的所有字符都不会改变。所以我们只需要将改变了的字符加入后缀自动机就好。至于从那里开始加,可以用栈维护那个起始点,每次插入一个字符,然后再从插入之后的终点开始插入下一个。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 7;


struct Suffix_Automata
{
    int maxlen[N << 1], trans[N << 1][12], link[N << 1], Size, Last;
    Suffix_Automata() { Size = Last = 1; }

    inline int Extend(int id, int last)
    {
        if(trans[last][id])//这里是专门添加的插入,除了有返回之外,剩下的与裸的后缀自动机一样
        {
            int q = trans[last][id], p = last;
            if(maxlen[q] == maxlen[last] + 1) return q;
            else
            {
                int clone = ++Size;
                maxlen[clone] = maxlen[last] + 1;
                memcpy(trans[clone], trans[q], sizeof(trans[clone]));
                link[clone] = link[q];
                for (; p && trans[p][id] == q; p = link[p]) trans[p][id] = clone;
                link[q] = clone;
                return clone;
            }
        }

        int cur = (++Size), p;
        maxlen[cur] = maxlen[last] + 1;
        for (p = last; p && !trans[p][id]; p = link[p]) trans[p][id] = cur;
        if (!p) link[cur] = 1;
        else
        {
            int q = trans[p][id];
            if (maxlen[q] == maxlen[p] + 1)
                link[cur] = q;
            else
            {
                int clone = (++Size);
                maxlen[clone] = maxlen[p] + 1;
                memcpy(trans[clone], trans[q], sizeof(trans[clone]));
                link[clone] = link[q];
                for (; p && trans[p][id] == q; p = link[p]) trans[p][id] = clone;
                link[cur] = link[q] = clone;
            }
        }
        last = cur;
        return cur;
    }

    struct node{
        int s, last;
    };
    stack<node> sta;
    void solve()
    {
        char s[N];
        scanf("%s", s + 1);
        int len = strlen(s + 1);
        for (int i = len; i >= 1;i--)//用栈维护第i个字符对之后后缀的改变,直到遇到大于等于x的字符(因为比它大的时候,后面的后缀与原先的相同)
        {
            int x = s[i] - 'a';
            int del = 1, st;

            while(sta.size() && sta.top().s < x) sta.pop(), del++;
            if(!sta.size()) st = 1;
            else st = sta.top().last;

            while(del--)
            {
                st = Extend(x, st), sta.push({x, st});
            }
        }
        ll ans = 0;
        for (int i = 2; i <= Size;i++)//遍历各个节点,统计答案
        {
            ans += maxlen[i] - maxlen[link[i]];
        }
        printf("%lld\n", ans);
    }
} T;


int main()
{
    T.solve();
    return 0;
}
posted @ 2020-07-21 10:43  Cyan_Cloud  阅读(141)  评论(0编辑  收藏  举报