[CF] D. AB-string - Educational Codeforces Round 74 (Rated for Div. 2)

https://codeforces.com/contest/1238/problem/D

题目大意

给出 $n$ 长的只包含 ‘AB’ 的字符串 $S$。

定义一个字符串是好的:字符串中所有字符都可以是某个(不同字符不用是同一个)长度至少为 $2$ 的回文串的一部分。

问 $S$ 有多少个好的子串。

$|S| \le 3 \times 10^5$。

简要题解

观察:

  1. 如果串中某个字符超过两个,则这些字符都可以在某些回文中,因为只要选择相邻的两个同种字符,那中间夹得都是另一种字符,则这一定是一个回文。
  2. 某个串不合法当且仅当有字符只出现了一次,且该字符在串的起始或者结尾。如果出现在中间显然它两侧会被两个另一种字符包裹,可以构成 $3$ 长的回文。

有了这些观察,我们就可以枚举右边界。假设最右的字符为 $c$,根据观察 2,左边界必须至少包含 $c$ 上一次出现的位置。 然后只需要讨论对于另一种字符 $d$ 的情况,假设目前最大左边界的段已经包含了一个或以上的另一种字符,则所有满足上述约束的左边界都合法。因为如果不出现下一个 $d$ 则 $d$ 的所有频率都包裹在中间。 如果出现了下一个 $d$ 则 $d$ 的频率超过 $2$ 次,也没有问题。如果当前最大左边界的段没有包含 $d$ 则下次第一次遇到 $d$ 时,这是唯一一个不合法的段,因为这个 $d$ 无法属于任何回文,但再左推左边界时,都合法,因为如果左边字符是 $d$ 则满足 1,如果左边字符时 $c$ 则与上一种情况讨论的一致了。

代码只需要保存一下两种字符之前的出现位置即可。(实际上不用存数组,因为都各只用到了上一个)

复杂度

$T$:$O(n)$

$S$:$O(n)$

代码实现

#include <bits/stdc++.h>
using namespace std;

int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();

using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;

template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) { 
    return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) { 
    return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z 
    if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
    if(x<z) { y=x; x=z; } else if(y<z) y=z; }

// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());

/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/

void solve() {
  int n;
  string s;
  cin >> n >> s;

  vector<vector<int>> pos(2);

  LL ans = 0;
  for (int i = 0; i < n; i++) {
    int c = s[i] - 'A';

    pos[c].push_back(i);

    if (pos[c].size() == 1) continue;

    if (pos[c ^ 1].empty()) {
      ans += i;
    } else {
      int r = pos[c][pos[c].size() - 2];

      ans += r + 1;
      if (pos[c ^ 1].back() < r) {
        ans--;
      }
    }

    // cerr << ans << ' ';
  }

  cout << ans << '\n';
}

int main() {
  int t = 1; 
  // cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}
Prev: [CF] C. Customer Service - Codeforces Round 1002 (Div. 2)
Next: [CF] A. LCM Problem - Educational Codeforces Round 92 (Rated for Div. 2)