CF-EDU30-F. Forbidden Indices
题目:F. Forbidden Indices
题意:给你长度为 \(n\) 的字符串 \(s\) ,并且标记一些位置为禁止位,定义一个字符串 \(t\) 的权值为:\( |t|*F(t)\) ,其中F表示 \(t\) 在 \(s\) 中出现,且不以禁止位结尾的次数。
算法:
字符串、后缀数组、单调栈、贪心
题解:
考虑把字符串调转,这样就变成了无法以禁止位开头。
答案的来源有两种:
(1)字符串 \(t\) 只出现了一次,这个时候当然是直接取后缀最优【此时最长】
(2)字符串 \(t\) 至少出现了两次,这个时候可以想到height数组,我们把相邻的两个后缀绑在一起,得到 \(lcp\),这是最优的(这一步有贪心的思想)。我们考虑使用单调栈扩展这个 \(lcp\)的区间 \([L,R]\) ,就像是二维最大矩阵那道例题一样【或者是柱状图最大矩阵】。
做法:直接后缀排序,来源一没什么细节,直接写。
重点考虑来源二,我们需要去掉那些禁止位的后缀再做单调栈,使用前缀和可以去掉禁止位的影响,所以需要额外维护一个 \(len\) 数组,表示 \(lcp\)的长度,最后相邻两个后缀的 \(lcp\) 对答案的影响为:\( sum[R[i]] - sum[L[i] - 1] ) * len[i] \)。
代码:
const int maxn = 2e5 + 17, maxm = 3e5 + 11, inf_int = 0x3f3f3f3f; // 注意,inf_int < 2^30, (1<<31)已经超了int const ll inf_ll = 0x3f3f3f3f3f3f3f, mod = 1e9L + 7; const double eps = 1e-6; int n, ork[maxn], rk[maxn], sa[maxn], osa[maxn]; int L[maxn], R[maxn], ct[maxn], ht[maxn], len[maxn]; char s[maxn], b[maxn]; inline bool cmp(int x, int y, int d) { if (ork[x] != ork[y]) return false; if (x + d > n || y + d > n) return x + d > n && y + d > n; return ork[x + d] == ork[y + d]; } inline void build() { int lim = 128, top = 0; for (int i = 1; i <= n; i++) ct[rk[i] = s[i]]++; for (int i = 1; i <= lim; i++) ct[i] += ct[i - 1]; for (int i = 1; i <= n; i++) sa[ct[rk[i]]--] = i; for (int d = 1; d < n; lim = top, d <<= 1) { top = 0; for (int i = n - d + 1; i <= n; i++) osa[++top] = i; for (int i = 1; i <= n; i++) if (sa[i] > d) osa[++top] = sa[i] - d; for (int i = 0; i <= lim; i++) ct[i] = 0; for (int i = 1; i <= n; i++) ct[rk[i]]++; for (int i = 1; i <= lim; i++) ct[i] += ct[i - 1]; for (int i = n; i; i--) sa[ct[rk[osa[i]]]--] = osa[i]; memcpy(ork, rk, sizeof(rk)), top = 0; for (int i = 1; i <= n; i++) rk[sa[i]] = (i > 1 && cmp(sa[i], sa[i - 1], d)) ? top : ++top; if (top == n) break; } for (int i = 1, k = 0; i <= n; i++) { if (k) k--; while (s[i + k] == s[sa[rk[i] - 1] + k]) k++; ht[rk[i]] = k; } } inline void solve() { cin >> n >> s + 1 >> b + 1; reverse(s + 1, s + 1 + n); reverse(b + 1, b + 1 + n); // cout << b + 1 << endl; build(), me(ct, 0), me(len, inf_int); ll ans = 0; for (int i = 1; i <= n; i++) { L[i] = 1, R[i] = n, ct[i] = ct[i - 1] + (b[sa[i]] == '0'); if (b[sa[i]] == '0') ans = max(ans, n - sa[i] + 1ll); } stackstk; for (int i = 2, las = 1; i <= n; i++) { if (b[sa[i]] == '1') continue; for (int j = las + 1; j <= i; j++) len[i] = min(len[i], ht[j]); while (stk.size() && stk.top().fi > len[i]) R[stk.top().se] = i - 1, stk.pop(); if (stk.size()) L[i] = stk.top().se; stk.push(mp(len[i], i)), las = i; } for (int i = 2; i <= n; i++) if (b[sa[i]] == '0') ans = max(ans, (ll)(ct[R[i]] - ct[L[i] - 1]) * len[i]); cout << ans << endl; }