1. 程式人生 > 其它 >CF-EDU30-F. Forbidden Indices

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); } stack<PII> stk; 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; }