题解:P11823 [湖北省选模拟 2025] 最后的台词 / lines
简单题,但是深刻意识到了字符串的尽头是数据结构。
题意:给出一个串 \(S\),定义一个 \(k\) 连续序列 \(T\) 需要满足以下性质:
- 所有串 \(T_i\) 都是 \(S\) 子串。
- 相邻两个串 \(T_i, T_{i+1}\) 满足 \(T_i\) 长为 \(k\) 的后缀和 \(T_{i+1}\) 长为 \(k\) 的前缀相同。
现在询问 \(q\) 次,每次询问 \(T_1=S_{l_1\cdots r_1},T_{m}=S_{l_2,r_2}\) 的 \(k\) 连续序列最小长度 \(m\) 是多少。
做法:
先特判答案为 \(1,2\) 的,可以直接 hash 计算。
首先先思考 \(k\) 是固定的怎么做,我们只在乎 \(k\) 长子串的事情,考虑把所有长度为 \(k\) 的子串拉出来,我们在每一个子串末尾的位置有一个点。那么我们发现我们可以以 \(0\) 的代价直接到任意一个当前串一样的。可以以 \(1\) 的代价直接到任意一个在当前串后面出现的串,发现在这种代价下,我们一定是往后跳到某个串然后跳到这个串出现第一次的位置,因为在前面一定比后面更优。那么我们记 \(f_i\) 代表 \(i\) 用代价 \(1\) 可以到的最小的位置。可以发现除了自环会形成一个森林的结构,直接倍增维护,从 \(r_1\) 往前跳即可,跳到标号 \(\le l_1+k-1\) 的位置。注意因为我还需要 \(T_1,T_m\) 还有一个衔接的串,所以答案应该是跳的次数 \(+3\)。
然后考虑 \(k\) 不定怎么做,我们考虑让 \(k\) 从大往小扫,同时维护这个森林的结构。那么我们把 SAM 建出来,考虑 endpos 集合,当 \(k\rightarrow k-1\) 的时候,某些串会合并,我们发现,对于一个串出现在 \(a_1<a_2<\cdots <a_t\) de 位置时,我们在森林上 \([a_1,a_t]\) 的父亲会对 \(a_1\) 取 \(\min\)。什么时候会合并呢?就是在 \(k\) 等于 SAM 某个节点的最长串长时就会合并,这个时候就要做一次更新父亲的操作。提前预处理这个 \(a_1,a_t\)。
那么现在问题转化为,每次对区间取 \(\min\),询问从 \(x\) 开始跳 \(\le y\) 的次数。考虑用分块维护,散块暴力取 \(\min\),类似于弹飞绵羊处理,维护跳出块的步数。注意到区间取 \(\min\) 是对区间左端点,所以很好的是我们如果对一个整块打标记,那么我们一次跳跃一定会跳出去,可以比较方便计算跳一次的贡献。
回答询问就直接往块外跳,跳过了就暴力跳就行,复杂度 \(O(q\sqrt n)\),但是因为这个题还有一层字符串的原因所以其实常数特别小。
还有一个细节是,询问时我们可以以 \(0\) 代价跳到和 \(T_1\) 后缀一样的,\(T_m\) 类似可以到最后一个前缀一样的,这个需要在 SAM 的 endpos 树上跳就可以,具体可以见代码。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 5, bs = 131;
int n, q;
string s;
unsigned long long h[maxn], pw[maxn];
unsigned long long query(int l, int r) {
return (h[r] - h[l - 1] * pw[r - l + 1]);
}
struct node {
int nxt[26], lnk, len, l, r, ed;
node() {
l = 2e9, r = 0;
}
} ;
struct SAM {
node tr[maxn]; int tot = 1, lst = 1;
int nw[maxn], f[22][maxn];
inline void add(int c, int pos) {
int cur = ++tot, p = lst;
tr[cur].len = tr[p].len + 1;
tr[cur].l = tr[cur].r = pos;
while(p && !tr[p].nxt[c])
tr[p].nxt[c] = cur, p = tr[p].lnk;
if(!p)
tr[cur].lnk = 1;
else {
int q = tr[p].nxt[c];
if(tr[p].len + 1 == tr[q].len)
tr[cur].lnk = q;
else {
int clone = ++tot;
tr[clone] = tr[q]; tr[clone].len = tr[p].len + 1;
tr[clone].l = 2e9, tr[clone].r = 0;
while(p && tr[p].nxt[c] == q)
tr[p].nxt[c] = clone, p = tr[p].lnk;
tr[q].lnk = tr[cur].lnk = clone;
}
}
nw[pos] = cur;
// cout << p << " " << tr[cur].lnk << " " << cur << endl;
lst = cur;
}
vector<int> p[maxn];
vector<int> e[maxn];
void prepare1() {
for (int i = 1; i <= tot; i++)
e[tr[i].lnk].push_back(i), f[0][i] = tr[i].lnk;
for (int j = 1; j <= 21; j++)
for (int i = 1; i <= tot; i++)
f[j][i] = f[j - 1][f[j - 1][i]];
}
void dfs(int u) {
tr[u].ed = tr[u].len;
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
dfs(v);
tr[u].l = min(tr[u].l, tr[v].l);
tr[u].r = max(tr[u].r, tr[v].r);
}
// cout << u << " " << tr[u].len << " " << tr[u].ed << " " << tr[u].l << "ASD " << tr[u].r << " " << tr[tr[u].lnk].len << endl;
}
void prepare() {
for (int i = 2; i <= tot; i++)
p[tr[i].ed].push_back(i);
}
inline int get_pos(int p, int l) {
if(tr[tr[nw[p]].lnk].len < l)
return nw[p];
for (int i = 21; i >= 0; i--)
if(tr[f[i][nw[p]]].len >= l)
nw[p] = f[i][nw[p]];
return nw[p];
}
} tree;
int l1[maxn], r1[maxn], l2[maxn], r2[maxn], ans[maxn];
vector<int> qry[maxn];
const int inf = 2e9;
struct Block {
int pre[maxn], a[maxn], l[maxn], r[maxn], pos[maxn], tag[maxn], B = 400, dis[maxn];
void build_block(int p) {
if(tag[p] < inf)
return ;
for (int i = l[p]; i <= r[p]; i++) {
if(a[i] == i)
pre[i] = i, dis[i] = 0;
else if(a[i] < l[p])
pre[i] = a[i], dis[i] = 1;
else
pre[i] = pre[a[i]], dis[i] = dis[a[i]] + 1;
}
}
void prepare() {
for (int i = 1; i <= n; i++) {
pos[i] = (i - 1) / B + 1;
if(!l[pos[i]])
l[pos[i]] = i;
r[pos[i]] = i;
a[i] = i;
}
for (int i = 1; i <= pos[n]; i++)
tag[i] = inf, build_block(i);
}
inline void change(int lx, int rx, int v) {
if(pos[lx] == pos[rx]) {
if(tag[pos[lx]] < lx)
return ;
for (int i = lx; i <= rx; i++)
a[i] = min(a[i], v);
build_block(pos[lx]);
}
else {
for (int i = lx; i <= r[pos[lx]]; i++)
a[i] = min(a[i], v);
for (int i = l[pos[rx]]; i <= rx; i++)
a[i] = min(a[i], v);
for (int i = pos[lx] + 1; i <= pos[rx] - 1; i++)
tag[i] = min(tag[i], v);
build_block(pos[lx]), build_block(pos[rx]);
}
}
inline int get_nxt(int x) {
return min(tag[pos[x]], pre[x]);
}
inline int query(int x, int y) {
if(x >= y)
return 0;
int res = 0;
while(1) {
if(tag[pos[y]] != inf) {
int to = min(tag[pos[y]], a[y]);
y = to, res++;
}
else if(pre[y] >= x && (pre[y] != y))
res += dis[y], y = pre[y];
else {
while(1) {
int to = min(tag[pos[y]], a[y]);
if(y == to)
return -4;
y = to; res++;
// cout << x << " " << y << endl;
if(x >= y)
break;
}
break;
}
if(y <= x)
return res;
// cout << x << " " << y << " " << p << endl;
}
return res;
}
} B;
int read() {
int sum = 0; char c = getchar();
while(!isdigit(c))
c = getchar();
while(isdigit(c))
sum = sum * 10 + c - '0', c = getchar();
return sum;
}
void write(int x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x <= 9) {
putchar(x + '0');
return ;
}
write(x / 10);
putchar(x % 10 + '0');
}
signed main() {
// freopen("test.in", "r", stdin);
// freopen("std.out", "w", stdout);
cin >> s, q = read(), n = s.size(), s = ' ' + s;
pw[0] = 1;
// cout << q << endl;
for (int i = 1; i <= n; i++)
pw[i] = pw[i - 1] * bs, h[i] = h[i - 1] * bs + s[i] - 'a' + 1;
for (int i = 1; i <= n; i++)
tree.add(s[i] - 'a', i);
tree.prepare1();
tree.dfs(1), tree.prepare();
for (int i = 1; i <= q; i++) {
int k;
l1[i] = read(), r1[i] = read(), l2[i] = read(), r2[i] = read(), k = read();
//cout << query(l1[i], r1[i]) << " " << query(l2[i], r2[i]) << endl;
if(query(l1[i], r1[i]) == query(l2[i], r2[i]))
ans[i] = 1;
else if(query(r1[i] - k + 1, r1[i]) == query(l2[i], l2[i] + k - 1))
ans[i] = 2;
else
qry[k].push_back(i);
}
B.prepare();
for (int i = n; i >= 1; i--) {
for (int j = 0; j < tree.p[i].size(); j++) {
B.change(tree.tr[tree.p[i][j]].l, tree.tr[tree.p[i][j]].r, tree.tr[tree.p[i][j]].l);
}
// cout << i << endl;
for (int j = 0; j < qry[i].size(); j++) {
int bg = tree.get_pos(r1[qry[i][j]], i); bg = tree.tr[bg].l;
int ed = tree.get_pos(l2[qry[i][j]] + i - 1, i); ed = tree.tr[ed].r;
ans[qry[i][j]] = B.query(ed, bg) + 3;
}
// cout << i << endl;
}
for (int i = 1; i <= q; i++)
write(ans[i]), putchar('\n');
return 0;
}
/*
aba
0
*/

浙公网安备 33010602011771号