【题解】[LNOI2022] 串
\(T_0\) 是 \(S\) 的子串;
\(\forall 1 \le i \le l\),\(\lvert T_i \rvert - \lvert T_{i - 1} \rvert = 1\);
\(\forall 1 \le i \le l\),存在 \(S\) 的一个长度为 \(\lvert T_i \rvert + 1\) 的子串 \(S'_i\),使得 \(S'_i\) 的长度为 \(\lvert T_{i - 1} \rvert\) 的前缀为 \(T_{i - 1}\),长度为 \(\lvert T_i \rvert\) 的后缀为 \(T_i\)。
求最大的 \(l\)。
看起来这个限制条件非常怪异,我们手动模拟一下,发现就是扩展串的过程。从当前串 \([l,r]\),删除第一个位置,向后扩展两个位置,得到 \([l + 1, r + 2]\)。
所以我们肯定是从空串开始扩展最优,直接从 \(S\) 开始扩展可以得到 \(|S|/2\) 的答案。
考虑存在两个子串相同的情况,比如 \([l_1,r_1]\) 和 \([l_2, r_2]\),不妨设 \(l1 < l2\),那么我们扩展到 \([l_2,x] (x \le r_2)\) 后,可以跳到 \([l_1,r_1]\) 继续向后扩展,然后再次到达 \(l_2\),跳回到 \(l_1\)。一直持续到长度 \(> r - l + 1\) 结束。
那么最优的情况就是跳回到 \([l_1,r_1]\) 时,长度恰好为 \(r-l+1\),此时答案是 \(r - l + 1 + \left\lfloor\dfrac{|S| - r}{2}\right\rfloor\)。
如果使用多对相同的子串呢?我们观察到最优情况下,长度恰好取到 \(r-l+1\) 且一定能取到,所以使用多对子串不会比只使用最后一对子串更优,所以只用考虑一对相同子串的情况。
这些都可以使用后缀自动机快速求出,时间复杂度 \(\mathcal{O}(|S|)\)。
#define N 1000005
struct SAM{
int fa[N], nxt[N][26], sz[N], len[N], w[N], lst, idx;
void init(){
rep(i, 0, idx)
fa[i] = sz[i] = w[i] = 0, memset(nxt[i], 0, sizeof(nxt[i]));
fa[0] = ~0, lst = idx = 0;
}
void extend(int ch){
int cur = ++idx, p = lst; len[cur] = w[cur] = len[p] + 1;
while(~p && !nxt[p][ch])nxt[p][ch] = cur, p = fa[p];
if(-1 == p)fa[cur] = 0;
else{
int q = nxt[p][ch];
if(len[p] + 1 == len[q])fa[cur] = q;
else{
int now = ++idx; len[now] = len[p] + 1, w[now] = w[q];
rep(i, 0, 25)nxt[now][i] = nxt[q][i];
fa[now] = fa[q], fa[q] = fa[cur] = now;
while(~p && nxt[p][ch] == q)nxt[p][ch] = now, p = fa[p];
}
}sz[lst = cur]++;
}
int c[N], b[N];
void calc(){
rp(i, idx)c[i] = 0;
rp(i, idx)c[len[i]]++;
rp(i, idx)c[i] += c[i - 1];
rp(i, idx)b[c[len[i]]--] = i;
pr(i, idx)sz[fa[b[i]]] += sz[b[i]];
int n = len[lst], ans = n / 2;
rp(i, idx)if(sz[i] > 1)cmx(ans, len[i] + (n - w[i]) / 2);
printf("%d\n", ans);
}
}w;
char s[N];
int main() {
int T; read(T);
while(T--){
w.init();
scanf("%s", s + 1);
int n = strlen(s + 1);
rp(i, n)w.extend(s[i] - 'a');
w.calc();
}
return 0;
}