【洛谷5284】[十二省联考2019] 字符串问题(后缀树优化建边)

题目:

洛谷 5284

分析:

首先不要问我标题里的「后缀树」是什么,我也不会,瞎写的 …… (传说就是反串后缀自动机的 fa 树?)

前置技能:【知识总结】后缀自动机的构建

首先有一个很 naive 的想法:要求的 \(T\) 是由若干个 \(A_i\) 串拼接而成的,所以可以处理出对于每个 \(A_i\) ,后面接哪些 \(A_j\) 是合法的。将每个 \(A\) 串看成一个权值为串长的点, \(A_i\) 后面可以接 \(A_j\) 看成一条边 \((i,j)\) ,这样能建出一个 DAG ,DP 出最长路就是答案。一个小优化是把每个 \(B\) 串也看作一个点(权值为 \(0\) ),用后缀自动机之类的东西暴力处理出每个 \(B_i\) 是哪些 \(A_j\) 的前缀并连边,同时每个 \(A\) 向其支配的 \(B\) 连边。但这样边数最坏仍然是 \(O(n^2)\) 的。

等等,刚才好像写了个「后缀自动机」?首先,可以通过树上倍增求出每个 \(A\)\(B\) 串在后缀自动机上的点。「 \(B_i\)\(A_j\) 的前缀」在「后缀」自动机上不太好搞,不如用 \(S\) 的反串来建 SAM ,这个条件就变成了 \(B_i\)\(A_j\) 的后缀。那么,如果一个点 \(A_j\)\(B_i\) 所在结点的子树( fa 树)内,说明 \(B_i\)\(A_j\) 的后缀,需要连边。由此可知,我们需要从上至下连出 fa 树(即每个结点的 fa 向这个结点连边),然后每个 \(B\) 串向对应结点连边,每个结点向这个结点上的 \(A\) 串连边。

但是这样做有一个小瑕疵:如果 \(A_i\)\(B_j\) 在同一个结点上,那么它们之间是否连边取决于它们的长短关系,即这些串中短的一定是长的的后缀。因此,较长的 \(B_j\) 能连到的 \(A_i\) ,较短的 \(B_k\) 也一定能连到。此时的做法是把 \(B\) 按照从短到长连成一条链,然后对于 \(A_i\) ,找出最长的长度不超过它的 \(B_j\) ,并由 \(B_j\)\(A_i\) 连边。注意,既然如此,上文中说的「所在结点的子树」就不包含这个点本身(因为这个点上的 \(A\) 不一定全部满足),所以需要把树上的每个结点拆成出点和入点,入点向出点连,父亲的出点向儿子的入点连,入点向 \(A\) 连,\(B\) 向出点连。

代码:

恐龙给说的条件编译真好用

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
#include <queue>
using namespace std;

namespace zyt
{
    template<typename T>
    inline bool read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != EOF && c != '-' && !isdigit(c));
        if (c == EOF)
            return false;
        if (c == '-')
            f = true, c = getchar();
        do
            x = x * 10 + c - '0', c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
        return true;
    }
    inline bool read(char *const s)
    {
        return ~scanf("%s", s);
    }
    template<typename T>
    inline void write(T x)
    {
        static char buf[20];
        char *pos = buf;
        if (x < 0)
            putchar('-'), x = -x;
        do
            *pos++ = x % 10 + '0';
        while (x /= 10);
        while (pos > buf)
            putchar(*--pos);
    }
    typedef long long ll;
    const int N = 2e5 + 10, P = N * 6, M = N * 8, LOG = 20, CH = 26;
    const bool A = true, B = false;// B should be front of A
    int head[P], ecnt, n, m, na, nb, w[P];
    struct edge
    {
        int to, next;
    }e[M];
    void add(const int a, const int b)
    {
        e[ecnt] = (edge){b, head[a]}, head[a] = ecnt++;
    }
    typedef pair<int, bool> pib;
    typedef pair<pib, int> ppi;
    vector<ppi> v[N << 1];
    namespace Suffix_Auto_Chicken
    {
        struct node
        {
            int fa, max, s[CH];
        }tree[N << 1];
        int cnt, last, pos[N];
        void init()
        {
            memset(tree, 0, sizeof(node[cnt + 1]));
            for (int i = 0; i <= cnt; i++)
                v[i].clear();
            cnt = last = 1;
        }
        inline int ctoi(const char c)
        {
            return c - 'a';
        }
        void insert(const char c, const int id)
        {
            int x = ctoi(c);
            int np = ++cnt, p = last;
            tree[np].max = tree[p].max + 1;
            while (p && !tree[p].s[x])
                tree[p].s[x] = np, p = tree[p].fa;
            if (!p)
                tree[np].fa = 1;
            else
            {
                int q = tree[p].s[x];
                if (tree[p].max + 1 == tree[q].max)
                    tree[np].fa = q;
                else
                {
                    int nq = ++cnt;
                    memcpy(tree[nq].s, tree[q].s, sizeof(int[CH]));
                    tree[nq].fa = tree[q].fa;
                    tree[np].fa = tree[q].fa = nq;
                    tree[nq].max = tree[p].max + 1;
                    while (p && tree[p].s[x] == q)
                        tree[p].s[x] = nq, p = tree[p].fa;
                }
            }
            last = np;
            pos[id] = np;
        }
        int buf[N << 1], fa[N << 1][LOG];
        void topo()
        {
            static int count[N];
            int mx = 0;
            memset(count, 0, sizeof(count));
            for (int i = 1; i <= cnt; i++)
                ++count[tree[i].max], mx = max(mx, tree[i].max);
            for (int i = 1; i <= mx; i++)
                count[i] += count[i - 1];
            for (int i = 1; i <= cnt; i++)
                buf[count[tree[i].max]--] = i;
        }
        void build(const char *const s)
        {
            init();
            for (int i = 0; i < n; i++)
                insert(s[i], i);
            topo();
            for (int i = 1; i <= cnt; i++)
            {
                fa[buf[i]][0] = tree[buf[i]].fa;
                for (int j = 1; j < LOG; j++)
                    fa[buf[i]][j] = fa[fa[buf[i]][j - 1]][j - 1];
            }
        }
        int get(const int l, const int r)
        {
            int u = pos[r];
            for (int i = LOG - 1; i >= 0; i--)
                if (fa[u][i] && tree[fa[u][i]].max >= r - l + 1)
                    u = fa[u][i];
            return u;
        }
    }
    char s[N];
    using namespace Suffix_Auto_Chicken;
    ll solve(const int n)
    {
        static queue<int> q;
        static int in[P];
        static ll f[P];
        static bool vis[P];
        while (!q.empty())
            q.pop();
        memset(in, 0, sizeof(int[n + 1]));
        memset(vis, 0, sizeof(bool[n + 1]));
        memset(f, 0, sizeof(ll[n + 1]));
        for (int i = 0; i < ecnt; i++)
            ++in[e[i].to];
        for (int i = 1; i <= n; i++)
            if (!in[i])
                q.push(i), f[i] = w[i];
        ll ans = 0;
        while (!q.empty())
        {
            int u = q.front();
            q.pop();
            ans = max(ans, f[u]);
            for (int i = head[u]; ~i; i = e[i].next)
            {
                int v = e[i].to;
                if (vis[v])
                    return -1;
                f[v] = max(f[v], f[u] + w[v]);
                if (!(--in[v]))
                    q.push(v), vis[v] = true;
            }
        }
        for (int i = 1; i <= n; i++)
            if (in[i])
                return -1;
        return ans;
    }
    int work()
    {
        int T;
        read(T);
        while (T--)
        {
            read(s), n = strlen(s);
            reverse(s, s + n);
            build(s);
            for (int i = 1; i <= cnt; i++)
                w[i] = w[i + cnt] = 0;
            read(na);
            for (int i = 1; i <= na; i++)
            {
                int l, r;
                read(l), read(r);
                l = n - l, r = n - r, swap(l, r);
                v[get(l, r)].push_back(ppi(pib(r - l + 1, A), i));
                w[i + cnt * 2] = r - l + 1;
            }
            read(nb);
            for (int i = 1; i <= nb; i++)
            {
                int l, r;
                read(l), read(r);
                l = n - l, r = n - r, swap(l, r);
                v[get(l, r)].push_back(ppi(pib(r - l + 1, B), i));
                w[i + cnt * 2 + na] = 0;
            }
            memset(head, -1, sizeof(int[na + nb + cnt * 2 + 1]));
            ecnt = 0;
            for (int i = 1; i <= cnt; i++)
            {
                sort(v[i].begin(), v[i].end());
                add(i + cnt, i);
                if (tree[i].fa)
                    add(tree[i].fa, i + cnt);
                int last = -1;
                for (vector<ppi>::iterator it = v[i].begin(); it != v[i].end(); it++)
                {
                    if (it->first.second == A)
                    {
                        add(i + cnt, it->second + cnt * 2);
                        if (~last)
                            add(last + cnt * 2 + na, it->second + cnt * 2);
                    }
                    else
                    {
                        add(it->second + cnt * 2 + na, i);
                        if (~last)
                            add(last + cnt * 2 + na, it->second + cnt * 2 + na);
                        last = it->second;
                    }
                }
            }
            read(m);
            while (m--)
            {
                int a, b;
                read(a), read(b);
                add(a + cnt * 2, b + cnt * 2 + na);
            }
            write(solve(cnt * 2 + na + nb)), putchar('\n');
        }
        return 0;
    }
}
int main()
{
#ifdef BlueSpirit
    freopen("5284.in", "r", stdin);
#endif
    return zyt::work();
}

转载于:https://www.cnblogs.com/zyt1253679098/p/10912559.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值