Gym101821D Search Engine 题解
一、题目:
二、思路:
考虑使用后缀自动机。我们发现在后缀自动机上的一个节点 \(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;
}