CF906E
题意
给字符串 \(s,\ t\)。
问选出最少的 \(k\) 个互不相交的 \(t\) 中的子串 \(t[l_i,\ r_i]\) 并翻转使得 \(s\ =\ t\)。
\(1\ \leq\ |s|\ =\ |t|\ \leq\ 5\ *\ 10^5\)
做法1
构造字符串 \(str\ =\ s_1t_1s_2t_2...s_nt_n\)。
则如果 \(t[i...j]^R\ =\ s[i...j]\) 就等价于 \(str[2i\ -\ 1...2j]\) 为回文串。
于是问题等价于将 \(str\) 拆分成若干个长度为偶数的回文子串,并最小化长度 \(>\ 2\) 的回文串个数。
构建回文树,定义 \(anc_u\ =\ (len_u\ -\ len_{par_u}\ ==\ len_{par_u}\ -\ len_{par_{par_u}}\ ?\ anc_{par_u}\ :\ u)\),即分成 log 段等差数列。定义 \(S_{u}\ =\ \{v\ |\ anc_v\ =\ anc_u,\ v\ =\ par(par(...par(u)))\}\)。
在进行dp转移的时候每次 \(f_i\ =\ min_{v\ \in\ S_u}\{f_{i\ -\ len_v}\ +\ 1\}\),其中 \(u\) 为一个 \(S\) 中的深度最深的节点。可以发现对于可以转移到 \(i\) 的所有 \(v\) 会在转移到 \(i\ -\ (len_{par_u}\ -\ len_u)\) 的时候就被算了一遍。于是我们记录 \(rem_u\) 表示在 \(i\) 从 \(1\) 扫到 \(n\) 的时候最大的 \(S_u\) 转移到最后一个 \(i\) 的值。可以证明在 \(i\ -\ (len_{par_u}\ -\ len_u)\) 到 \(i\) 之间的左右 \(i\) 均不会在遍历到 \(u\)。
代码
#include <bits/stdc++.h>
#ifdef DEBUG
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif
#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif
using namespace std;
const int maxn = 1e6 + 10, oo = 1e9;
char s[maxn], t[maxn];
int n, f[maxn], g[maxn], m, len[maxn], par[maxn], anc[maxn], remf[maxn], remg[maxn], go[maxn][26];
int main() {
scanf("%s", t); n = strlen(t); for (int i = 0; i < n; ++i) s[(i << 1) | 1] = t[i];
scanf("%s", t); for (int i = 0; i < n; ++i) s[i + 1 << 1] = t[i]; n <<= 1;
len[0] = -(m = 1); remf[0] = remf[1] = oo; anc[0] = 0; anc[1] = 1;
for (int lst = 0, i = 1; i <= n; ++i) {
f[i] = oo;
while(s[i - len[lst] - 1] != s[i]) lst = par[lst];
if(!go[lst][s[i] - 'a']) {
go[lst][s[i] - 'a'] = ++m;
len[m] = len[lst] + 2;
if(!lst) par[m] = 1;
else {
int x = par[lst];
while(s[i - len[x] - 1] != s[i]) x = par[x];
par[m] = go[x][s[i] - 'a'];
}
anc[m] = ((par[m] > 1 && len[m] - len[par[m]] == len[par[m]] - len[par[par[m]]]) ? anc[par[m]] : m);
}
lst = go[lst][s[i] - 'a'];
for (int u = lst; u > 1; u = par[anc[u]]) {
remf[u] = f[i - len[anc[u]]]; remg[u] = i - len[anc[u]];
if(anc[u] != u && remf[par[u]] < remf[u]) remf[u] = remf[par[u]], remg[u] = remg[par[u]];
if((~i & 1) && f[i] > remf[u] + 1) f[i] = remf[u] + 1, g[i] = remg[u];
}
if((~i & 1) && s[i - 1] == s[i] && f[i - 2] < f[i]) f[i] = f[i - 2], g[i] = i - 2;
}
if(f[n] == oo) { puts("-1"); return 0; }
printf("%d\n", f[n]);
for (int i = n; i; i = g[i]) {
if(i - g[i] > 2) printf("%d %d\n", (g[i] >> 1) + 1, i >> 1);
}
return 0;
}