题目描述
难度分:1300
输入一个长度≤106的字符串s,只包含小写字母v
和o
。
把s中的两个相邻的v
视作一个w
。
输出有多少个wow
子序列。
注意:子序列不一定连续。子序列中的w
必须由s中的相邻v
组成。
输入样例1
vvvovvv
输出样例1
4
输入样例2
vvovooovovvovoovoovvvvovovvvov
输出样例2
100
算法
前后缀分解
这样统计三元组类型的题目一般就是枚举中间的元素,本题中就是枚举中间的o
可能在哪个位置。对于某个位置的o
,将o
左右两边的w
个数交叉组合用乘法原理乘起来即可。
这需要做一个预处理,pre[i]表示前缀[1,i]中有多少个w
,suf[i]表示后缀[i,n]中有多少个w
,状态转移如下:
- 如果s[i]=s[i−1]=
v
,则有pre[i]=pre[i−1]+1,否则pre[i]=pre[i−1]。 - 同理,如果s[i]=s[i+1]=
v
,则有suf[i]=suf[i+1]+1,否则suf[i]=suf[i+1]。
预处理出pre和suf两个数组后,再遍历所有满足s[i]=o
的位置,计算出i的贡献累加到答案上即可。
复杂度分析
时间复杂度
遍历了两次字符串,一次预处理,一次求答案,时间复杂度为O(n)。
空间复杂度
除了输入的字符串s外,开辟了pre和suf两个数组,空间消耗与s串同规模,因此额外空间复杂度为O(n)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1000010;
int n, pre[N], suf[N];
char s[N];
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
for(int i = 2; i <= n; i++) {
int j = n - i + 1;
pre[i] = pre[i - 1] + (s[i] == 'v' && s[i - 1] == 'v'? 1: 0);
suf[j] = suf[j + 1] + (s[j] == 'v' && s[j + 1] == 'v'? 1: 0);
}
LL ans = 0;
for(int i = 3; i <= n - 2; i++) {
if(s[i] == 'o') {
ans += (LL)pre[i - 1] * suf[i + 1];
}
}
printf("%lld\n", ans);
return 0;
}