思路
注意到
对于一个 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;
}

京公网安备 11010502036488号