思路

注意到

对于一个 o 来说,它越靠字符串的两端(极左或极右),它能包住的 v 就越多,价值越大。

对于一个 v 来说,它越靠字符串的中间,它左边的 o 和右边的 o 就越均衡,价值越大

假设你在  ? 的替换中,搞出了一个 vov 的替换序列

  • 中间的那个 o 被夹在两个 v 中间,,位置比较吃亏。
  • 如果你把中间的 o 和外面的 v 互换位置(变成 o, v, v 或者 v, v, o):o 被移到了外面,可以作为更多 ovo 的前缀或后缀。v 被移到了中间,可以享受两边更多的 o。
  • 结论:任何交替的 v, o 组合,都能通过 “把 o 往两边赶,把 v 往中间聚” 来获得严格更大(或相等)的收益。

因此,所有 ? 被替换后的最终形态,必定是由左边一堆 o,中间一堆 v,右边一堆 o 组成的(即 oo...ovv...voo...o 模式)。

通过双重循环来模拟这种过程即可。

int jisuan(string &s)
{
    int a = 0, b = 0;
    int res = 0;
    // a:代表当前遍历到的 o 的数量。
    // b:代表当前遍历到的 ov 子序列的数量。
    // res:代表最终求得的 ovo 子序列的数量。
    for (char c : s)
    {
        if (c == 'o')
        {
            res += b; // 遇到第三个字符 'o',加上前面已经组成的 'ov' 的数量
            a++;      // 记录第一个字符 'o' 的数量
        }
        else
        {
            b += a; // 遇到第二个字符 'v',它能和前面所有的 'o' 组成 'ov'
        }
    }
    return res;
}
void solve()
{
    cin >> s;
    n = s.size();
    vi a;
    for (int i = 0; i < n; i++)
    {
        if (s[i] == '?')
        {
            a.push_back(i);
            s[i] = 'o';
        }
    }
    int ans = jisuan(s);
    m = a.size();
    for (int i = 0; i < m; i++)
    {
        for (int j = i; j < m; j++)
        {
            s[a[j]] = 'v';
            ans = max(ans, jisuan(s)); // 每次都更新一下
        }
        for (int j = i; j < m; j++)
        {
            s[a[j]] = 'o'; // 别忘了回溯回去
        }
    }

    cout << ans << endl;
}