题解:CF1913F Palindromic Problem
题意
给定长度为 \(n\) 的小写字母串 \(s\)。你可以将至多一个位置修改为任意小写字母,使得新串的回文子串数量最多,在此基础上,最小化新串的字典序。\(1\leq |s|\leq 3\times 10^5\)。
题解
居然没有调试一遍过了,有点帅。
比较无聊的题。
先对 \(s\) 跑一次 Manacher 求出回文半径,全部相加可以得到回文子串数量。
考虑预处理出 \(f_{i,j}\) 表示将 \(s_i\) 修改为 \(j\) 的增量。枚举回文中心(可以是字符或字符间的间隔),我们知道一个中心对答案的贡献是其回文半径,因此考察修改某个位置对该中心的回文半径的影响。不妨假设回文中心是 \(s_i\),回文半径为 \(d_i\),可以发现,只有把 \(j=i-d_i\lor j=i+d_i\lor j\in[i-d_i+1,i)\lor j\in(i,i+d_i-1]\) 的 \(s_j\) 修改为和原来不同的字符回文半径才会改变。分类讨论:
- \(j=i-d_i\lor j=i+d_i\):必须要令 \(s_{i-d_i}\gets s_{i+d_i}\) 或令 \(s_{i+d_i}\gets s_{i-d_i}\) 才会使得 \(d_i\) 改变(当然也要保证 \(s_{i-d_i}\neq s_{i+d_i}\))。设 \(|\operatorname{lcp}(s_{i+d_i+1,n},\operatorname{rev}(s_{1,i-d_i-1}))|=t\),则此时对答案的增量为 \(t+1\)。对 \(s+\texttt{#}+\operatorname{rev}(s)\) 做一遍 SA,用 ST 表维护 \(ht\) 即可 \(\mathcal{O}(n\log{n})-\mathcal{O}(1)\) 维护。
- \(j\in[i-d_i+1,i)\lor j\in(i,i+d_i-1]\):回文半径显然减小。若 \(j\in[i-d_i+1,i)\) 则对答案贡献 \(-(j-i+d_i)\),否则若 \(j\in(i,i+d_i-1]\) 则对答案负贡献 \(-(i+d_i-j)\)。显然是区间加等差数列的形式,二阶差分简单维护。
回文中心为字符间隔的 case 基本一样,这里不再赘述。
预处理出 \(f_{i,j}\) 后就很简单了。比较两个二元组时,显然优先选 \(f\) 值最大的,若 \(f\) 值相同做一些简单讨论使得新串的字典序最小即可。时间复杂度 \(\mathcal{O}(n\log{n})\)。
代码
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) ((x) & -(x))
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const int N = 6e5 + 5, LGN = 20, C = 26;
const ll INF = 1e18;
template<typename T> inline void chk_min(T &x, T y) { x = min(x, y); }
template<typename T> inline void chk_max(T &x, T y) { x = max(x, y); }
int n, d0[N], d1[N];
ll sum, f[N][C], c[N];
pii ans;
string s, revs;
struct ST {
int mn[LGN][N];
void build(int n) {
for (int i = 1; i <= (31 ^ __builtin_clz(n)); ++i) for (int j = 1; j <= n - (1 << i) + 1; ++j)
mn[i][j] = min(mn[i - 1][j], mn[i - 1][j + (1 << i - 1)]);
}
int query(int l, int r) {
int k = 31 ^ __builtin_clz(r - l + 1);
return min(mn[k][l], mn[k][r - (1 << k) + 1]);
}
} st;
struct SA {
int n, buc[N], rk[N], trk[N << 1], id[N], rid[N], sa[N], ht[N];
void build(const string &s) {
n = s.size() - 1;
int m = 'z';
for (int i = 1; i <= n; ++i) ++buc[rk[i] = s[i]];
for (int i = 1; i <= m; ++i) buc[i] += buc[i - 1];
for (int i = n; i; --i) sa[buc[rk[i]]--] = i;
for (int w = 1, p = 0; p < n; w <<= 1, m = p) {
p = 0;
for (int i = n - w + 1; i <= n; ++i) id[++p] = i;
for (int i = 1; i <= n; ++i) if (sa[i] > w) id[++p] = sa[i] - w;
fill(buc + 1, buc + m + 1, 0);
for (int i = 1; i <= n; ++i) ++buc[rid[i] = rk[id[i]]];
for (int i = 1; i <= m; ++i) buc[i] += buc[i - 1];
for (int i = n; i; --i) sa[buc[rid[i]]--] = id[i];
copy(rk + 1, rk + n + 1, trk + 1), p = 0;
for (int i = 1; i <= n; ++i) rk[sa[i]] = p += trk[sa[i - 1]] != trk[sa[i]] || trk[sa[i - 1] + w] != trk[sa[i] + w];
}
for (int i = 1, j = 0; i <= n; ++i) {
if (rk[i] == 1) continue;
if (j) -- j;
while (j <= n - max(i, sa[rk[i] - 1]) && s[i + j] == s[sa[rk[i] - 1] + j]) ++j;
ht[rk[i]] = j;
}
for (int i = 1; i <= n; ++i) st.mn[0][i] = ht[i];
st.build(n);
}
int lcp(int x, int y) { return st.query(min(rk[x], rk[y]) + 1, max(rk[x], rk[y])); }
} sa;
void manacher() {
int l = 0, r = 0;
for (int i = 1; i <= n; ++i) {
d1[i] = i <= r ? min(d1[l + r - i], r - i + 1) : 0;
while (i > d1[i] && i + d1[i] <= n && s[i - d1[i]] == s[i + d1[i]]) ++d1[i];
sum += d1[i];
if (i + d1[i] - 1 > r) l = i - d1[i] + 1, r = i + d1[i] - 1;
}
l = r = 0;
for (int i = 1; i <= n; ++i) {
d0[i] = i <= r ? min(d0[l + r - 1 - i], r - i) : 0;
while (i > d0[i] && i + d0[i] < n && s[i - d0[i]] == s[i + d0[i] + 1]) ++d0[i];
sum += d0[i];
if (i + d0[i] > r) l = i - d0[i] + 1, r = i + d0[i];
}
}
void add(int l, int r, int s, int d) {
int t = s + (r - l) * d;
c[l] += s, c[l + 1] += d - s, c[r + 1] -= d + t, c[r + 2] += t;
}
bool cmp(const pii &a, const pii &b) {
auto [x, c1] = a; auto [y, c2] = b;
if (f[x][c1] ^ f[y][c2]) return f[x][c1] > f[y][c2];
if (x == y) return c1 < c2;
bool flag = x > y;
if (flag) swap(x, y), swap(c1, c2);
if (c1 ^ s[x]) return flag ^ (c1 < s[x]);
return flag ^ (c2 > s[y]);
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> s, revs = s, s = '#' + s;
reverse(revs.begin(), revs.end());
sa.build(s + '#' + revs), manacher();
for (int i = 1; i <= n; ++i) s[i] -= 'a';
for (int i = 1; i <= n; ++i) {
int l = i - d1[i] + 1, r = i + d1[i] - 1;
if (l > 1 && r < n && s[l - 1] != s[r + 1]) {
int d = 1 + (l - 2 >= 1 && r + 2 <= n ? sa.lcp(r + 2, sa.n - (l - 2) + 1) : 0);
f[l - 1][s[r + 1]] += d, f[r + 1][s[l - 1]] += d;
}
if (d1[i] > 1) add(l, i - 1, -1, -1), add(i + 1, r, l - i, 1);
if (i == n) continue;
l = i - d0[i] + 1, r = i + d0[i];
if (l > 1 && r < n && s[l - 1] != s[r + 1]) {
int d = 1 + (l - 2 >= 1 && r + 2 <= n ? sa.lcp(r + 2, sa.n - (l - 2) + 1) : 0);
f[l - 1][s[r + 1]] += d, f[r + 1][s[l - 1]] += d;
}
if (d0[i]) add(l, i, -1, -1), add(i + 1, r, l - i - 1, 1);
}
for (int i = 1; i <= n; ++i) c[i] += c[i - 1];
for (int i = 1; i <= n; ++i) c[i] += c[i - 1];
for (int i = 1; i <= n; ++i) for (int j = 0; j < C; ++j) if (s[i] ^ j) f[i][j] += c[i];
f[0][0] = -INF;
for (int i = 1; i <= n; ++i) for (int j = 0; j < C; ++j) ans = min(ans, {i, j}, cmp);
auto [x, y] = ans;
cout << sum + f[x][y] << '\n';
s[x] = y;
for (int i = 1; i <= n; ++i) cout << (char)(s[i] + 'a');
return 0;
}

浙公网安备 33010602011771号