【題解】[LNOI2022] 串
阿新 • • 發佈:2022-05-26
\(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]\)
所以我們肯定是從空串開始擴充套件最優,直接從 \(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\) 且一定能取到,所以使用多對子串不會比只使用最後一對子串更優,所以只用考慮一對相同子串的情況。
這些都可以使用字尾自動機快速求出,時間複雜度 \(\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; }