Description
有一个字符串 $S$,有 $n_a$ 个 A 类串是其子串,有 $n_b$ 个 B 类串是其子串,另外有 $m$ 个支配关系,表示第 $x$ 个 A 类串支配第 $y$ 个 B 类串。
求一个长度最大的字符串 $T$,需要满足两个条件:
- 其可以分割为若干个 A 类串;
- 不妨设 $T=t_1+t_2+t_3+…+t_k$,$t_i=A_{id_i}$,那么对于所有的 $t_i,t_{i+1}$,都存在一个 $A_{id_i}$ 支配的 B 类串,使得这个 B 类串是 $t_{i+1}$ 的前缀。
只需要输出长度的最大值即可,若可以无限长,输出 $-1$。
$|S|,n_a,n_b,m\leq2\times10^5$,多组数据,每个数据点中 $|S|,n_a,n_b,m$ 的总和不会超过单组数据限制的 $10$ 倍。
Solution
看到这个要求的东西以及这个支配关系,很容易想到尝试用图论模型描述这个题。
对于每个 A 类串,向其支配的 B 类串连边权为 A 类串长度的边,对于每个 B 类串,连向所有以这个 B 类串为前缀的 A 类串,边权为 $0$,这样目标串就对应这个图上的一个最长路再加上终点 A 类串的长度。
或者把 A 类串看作点权,求一个点权最大的路径。
暴力连是 $O(n^3)$,然后可以考虑用一个后缀数据结构,发现后缀树可以比较好描述这个东西。
我们找到每个 A 类串和 B 类串,对于支配关系直接连边,然后对于每个 B 类串,向其子树里所有 A 类串连边就好了。
这样是 $O(n^2)$ 的。
然后找到 A 类串和 B 类串这件事可以用倍增找:找到一个子串 $S[l:r]$,找到后缀 $l$ 在后缀树上对应的叶子结点,然后往上跳即可。这是 $O(n\log n)$ 的。
连边的话可以利用现成的后缀树,后缀树相当于已经帮我们连好边了。这是 $O(n)$ 的。
然后还有个问题是后缀树上的点是压缩过的,当同一个点对应长度不同的子串的时候,我们需要把它拆开,从长度小连向长度大即可。
需要注意的是,这么折腾完之后我们原来图上 A 连 B、B 连 A 的性质被破坏了,因此不能用上面说的点权的方法求答案,只能用边权来求。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cctype>
using namespace std;
namespace solve
{
const int maxn = 1e6 + 10;
typedef long long ll;
int read(int &x)
{
x = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
;
for (; isdigit(ch); ch = getchar())
x = x * 10 + ch - '0';
return x;
}
inline int lg(int x) { return 31 - __builtin_clz(x); }
int tot = 1, ch[maxn][26], father[maxn], mx[maxn], id[maxn], lst = 1;
void insert(int c, int i)
{
int p = ++tot, f = lst;
lst = p, id[i] = p, mx[p] = mx[f] + 1;
while (f && !ch[f][c])
ch[f][c] = p, f = father[f];
if (f == 0)
father[p] = 1;
else
{
int q = ch[f][c];
if (mx[q] == mx[f] + 1)
father[p] = q;
else
{
int nq = ++tot;
memcpy(ch[nq], ch[q], sizeof(ch[q])), father[nq] = father[q], mx[nq] = mx[f] + 1;
father[p] = father[q] = nq;
while (f && ch[f][c] == q)
ch[f][c] = nq, f = father[f];
}
}
}
namespace dag
{
struct edge
{
int y, w;
};
vector<edge> e[maxn];
int rd[maxn];
inline void add(int x, int y, int w) { e[x].push_back({y, w}), rd[y]++; }
ll f[maxn];
int val[maxn];
ll dp()
{
queue<int> q;
for (int i = 1; i <= tot; i++)
if (!rd[i])
q.push(i);
while (!q.empty())
{
int x = q.front();
q.pop();
for (auto k : e[x])
{
int v = k.y, w = k.w;
f[v] = max(f[v], f[x] + w);
if (--rd[v] == 0)
q.push(v);
}
}
for (int i = 1; i <= tot; i++)
if (rd[i])
return -1;
for (int i = 1; i <= tot; i++)
f[i] += val[i];
return *max_element(f + 1, f + 1 + tot);
}
}
vector<int> e[maxn];
int dep[maxn], ff[maxn][20];
void build()
{
for (int i = 2; i <= tot; i++)
e[father[i]].push_back(i);
}
void dfs1(int x, int fa)
{
dep[x] = dep[fa] + 1, ff[x][0] = fa;
for (int i = 1; i <= 18; i++)
ff[x][i] = ff[ff[x][i - 1]][i - 1];
for (int v : e[x])
dfs1(v, x);
}
vector<pair<int, int>> a[maxn]; // first : len, second : i
inline int jump(int x, int len)
{
for (int i = lg(dep[x]); i >= 0; i--)
if (mx[ff[x][i]] >= len)
x = ff[x][i];
return x;
}
char str[maxn];
int n, m;
pair<int, int> seg[maxn];
int p[maxn];
void dfs(int x, int fa)
{
if (fa)
dag::add(fa, x, 0);
sort(a[x].begin(), a[x].end());
a[x].erase(unique(a[x].begin(), a[x].end()), a[x].end());
int lst = x;
for (int i = 1; i < (int)a[x].size(); i++)
{
if (a[x][i].first != a[x][i - 1].first)
dag::add(lst, ++tot, 0), lst = tot;
p[a[x][i].second] = lst;
}
for (int v : e[x])
dfs(v, lst);
}
void clear()
{
for (int i = 1; i <= tot; i++)
dag::rd[i] = dag::f[i] = 0, dag::e[i].clear(), dag::val[i] = 0;
for (int i = 1; i <= tot; i++)
a[i].clear(), p[i] = 0, memset(ch[i], 0, sizeof(ch[i])), father[i] = id[i] = mx[i] = 0;
for (int i = 1; i <= tot; i++)
e[i].clear(), dep[i] = 0, memset(ff[i], 0, sizeof(ff[i]));
lst = tot = 1;
}
void main()
{
scanf("%s", str + 1);
int len = strlen(str + 1);
for (int i = len; i >= 1; i--)
insert(str[i] - 'a', i);
build(), dfs1(1, 0);
read(n);
for (int i = 1; i <= n; i++)
read(seg[i].first), read(seg[i].second);
read(m);
for (int i = n + 1; i <= n + m; i++)
read(seg[i].first), read(seg[i].second);
for (int i = 1; i <= n + m; i++)
{
p[i] = jump(id[seg[i].first], seg[i].second - seg[i].first + 1);
a[p[i]].emplace_back(seg[i].second - seg[i].first + 1, i);
}
dfs(1, 0);
for (int i = 1; i <= n; i++)
dag::val[p[i]] = seg[i].second - seg[i].first + 1;
int k;
read(k);
for (int i = 1; i <= k; i++)
{
int x, y;
read(x), read(y), dag::add(p[x], p[y + n], seg[x].second - seg[x].first + 1);
}
cout << dag::dp() << endl;
clear();
}
}
int main()
{
int T;
cin >> T;
while (T--)
solve::main();
}