1. 程式人生 > 實用技巧 >HEAT OJ #39. 【2020.9.3 NOIP模擬賽 T3】C

HEAT OJ #39. 【2020.9.3 NOIP模擬賽 T3】C

題意簡述

給一長為 \(10^6\) 的字串,求有多少子串能夠看作合法括號序列(要求配對的“括號”字母相同)。

題解

模擬賽的重大失誤 給了70還是很不錯的,總比寫掛爆零強

首先看到括號序列要想棧,能配對儘可能配對,這樣就可以做到 \(O(n^2)\) 了。

現在我們換一種思路,對於每一個字首,找合法字尾個數。顯然我們可以設 \(f(i)\) 表示以 \(i\) 為結尾的合法字尾個數,那麼 \(f(i) = f(p) + 1\),其中 \(p\) 表示最靠後的那個合法位置。若能快速求 \(p\),即可 \(O(n)\) DP。

我們現在的問題是,求 \(g(i,c)\)(在 \(i\) 及之前的第一個 \(c\)

在合法位置出現的 \(c\) 的位置),求出後就可以通過 \(g(i-1,s_i)\) 確定 \(p\) 了。考慮如何推 \(g(i,c)\),“合法”即那個位置之後的部分可以被看作合法括號序列,顯然這樣的位置是具有傳遞性的,我們可以直接從上一個第一個合法的位置直接複製過來。而上一個第一個合法的位置可以通過 \(p\) 求。

程式碼:

for (register int i = 1; i <= n; ++i) {
  int c = s[i] - 'a', pre = g[i - 1][c] - 1;
  g[i][c] = i;
  if (pre < 0)  continue;
  f[i] = f[pre] + 1;
  ans += f[i];
  memcpy(g[i], g[pre], sizeof(g[pre]));
  g[i][c] = i;
}

明明這種題我去年CSP還會呢怎麼現在不會了\kk