Gym101821D Search Engine 题解

一、题目:

codesforces原题

二、思路:

考虑使用后缀自动机。我们发现在后缀自动机上的一个节点 \(x\),如果向 \(x\) 的 suffix links 走,就相当于在 \(x\) 的前面添上了一些字符;如果向 \(x\) 的转移函数走,就相当于在 \(x\) 的后面添上了一个字符,不仅如此,如果我们只考虑每个状态中最长的子串,那么相当于是在 \(x\) 的后面添上字符后又在前面添上几个字符。

而且我们发现,对于 \(x\) 所对应的最长子串来说,只有沿它的 suffix links 走和沿它的转移函数走才有可能是最优的。这还是在应用后缀自动机的重要性质:一个状态中所有子串的 endpos 集合相等。既然相等,那么每次都取到最长的子串一定是最优的。

又发现后缀自动机(带上suffix links 边)是一个 DAG。于是拓扑排序DP即可。

三、启示:

牢记后缀自动机的重要性质!!!

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int maxn = 500005;

int n, las = 1, tot = 1, head[maxn << 1], idx, in[maxn << 1];
long long dp[maxn << 1];
char ch[maxn];

struct Trans {
    int ch[27];
    int len, fa, cnt;
}node[maxn << 1];

struct Edge {
    int y, next;
    Edge() {}
    Edge(int _y, int _next) : y(_y), next(_next) {}
}e[maxn << 1];

inline void extend(int s) {
    int v = las, z = ++tot; las = tot;
    node[z].len = node[v].len + 1; node[z].cnt = 1;
    for (; v && node[v].ch[s] == 0; v = node[v].fa) node[v].ch[s] = z;
    if (!v) node[z].fa = 1;
    else {
        int x = node[v].ch[s];
        if (node[x].len == node[v].len + 1) node[z].fa = x;
        else {
            int y = ++tot;
            node[y] = node[x]; node[y].cnt = 0;
            node[y].len = node[v].len + 1;
            node[x].fa = node[z].fa = y;
            for (; v && node[v].ch[s] == x; v = node[v].fa) node[v].ch[s] = y;
        }
    }
}

inline void connect(int x, int y) {
    e[++idx] = Edge(y, head[x]);
    ++ in[y];
    head[x] = idx;
}

void dfs(int x) {
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].y;
        dfs(y);
        node[x].cnt += node[y].cnt;
    }
    for (int j = 1; j <= 26; ++ j) {
        if (node[x].ch[j]) ++ in[node[x].ch[j]];
    }
}

inline void chkmax(long long& x, long long y) {
    x = y > x ? y : x;
}

queue<int>q;

int main() {
    freopen("c.in", "r", stdin);
    freopen("c.out", "w", stdout);
    scanf("%s", ch + 1);
    n = strlen(ch + 1);
    for (int i = 1; i <= n; ++i) {
        extend(ch[i] - 'a' + 1);
    }
    for (int i = 2; i <= tot; ++i) {
        connect(node[i].fa, i);
    }
    dfs(1);
    q.push(1);
    while (q.size()) {
        int x = q.front(); q.pop();
        chkmax(dp[x], dp[node[x].fa] + 1LL * node[x].cnt * (node[x].len - node[node[x].fa].len));
        for (int j = 1; j <= 26; ++ j) {
            if (node[x].ch[j]) {
                chkmax(dp[node[x].ch[j]], dp[x] + 1LL * node[node[x].ch[j]].cnt * (node[node[x].ch[j]].len - node[x].len));
                -- in[node[x].ch[j]];
                if (!in[node[x].ch[j]]) q.push(node[x].ch[j]);
            }
        }
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].y;
            -- in[y];
            if (!in[y]) q.push(y);
        }
    }
    printf("%lld\n", dp[las]);
    return 0;
}

posted @ 2021-05-12 21:43  蓝田日暖玉生烟  阅读(75)  评论(0编辑  收藏  举报