#P2968. 第3题-小红的red权值
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 15
            Accepted: 5
            Difficulty: 6
            
          
          
          
                       所属公司 : 
                              阿里
                                
            
                        
              时间 :2025年5月18日-菜鸟(算法岗)
                              
                      
          
 
- 
                        算法标签>思维          
 
第3题-小红的red权值
题目描述
给定长度为 n 的字符串 s(只包含字符 r、e、d),定义任何下标 i<j<k 且 s[i]='r', s[j]='e', s[k]='d' 的子序列 “red” 的权值为
|i-j| + |i-k| + |j-k|
计算所有这样的子序列权值之和。
解题思路
- 
化简权值公式
当 i<j<k 时,|i-j|+|i-k|+|j-k| = (j−i)+(k−i)+(k−j) = 2*(k−i)。
也就是说,每个 “r” 在位置 i 与每个 “d” 在位置 k 配对时,只要中间存在至少一个 “e”,就要累加 2*(k−i)×(中间 “e” 的个数)。 - 
在线统计
- 维护一个数组 
preE,preE[x]表示位置 0..x 上出现的 ‘e’ 的总数。 - 遍历字符串时:
- 遇到 ‘r’(下标 i)时,更新:
cntR+= 1sumI+= isumPE+= preE[i]sumIPI+= i * preE[i]
 - 遇到 ‘d’(下标 k)时,先算出 k 之前的 ‘e’ 数量 E = preE[k−1](k>0 时)
然后所有已见过的 ‘r’ 对这次 ‘d’ 的贡献是
最后答案累加 2 * contrib。contrib = E * (cntR * k - sumI) - (k * sumPE - sumIPI) 
 - 遇到 ‘r’(下标 i)时,更新:
 
 - 维护一个数组 
 - 
时间和空间
- 时间复杂度 O(n),只遍历一次并做常数次计算。
 - 空间复杂度 O(n) 用于存 preE,可以优化到 O(1) 在线维护。
 
 
代码实现
Python
def calc_red_weight(s):
    n = len(s)
    # preE[i] = 0..i 范围内 'e' 的数量
    preE = [0] * n
    for i, ch in enumerate(s):
        preE[i] = preE[i-1] + (ch == 'e') if i else (ch == 'e')
    cntR = sumI = sumPE = sumIPI = 0
    ans = 0
    for k, ch in enumerate(s):
        if ch == 'r':
            # 记录一个 r 的信息
            cntR += 1
            sumI += k
            sumPE += preE[k]
            sumIPI += k * preE[k]
        elif ch == 'd':
            if cntR == 0: continue
            E = preE[k-1] if k > 0 else 0
            # M = cntR, A = sumI, B = sumPE, C = sumIPI
            M, A, B, C = cntR, sumI, sumPE, sumIPI
            # 组合贡献
            contrib = E * (M * k - A) - (k * B - C)
            ans += 2 * contrib
    return ans
# 读入输出
if __name__ == '__main__':
    import sys
    data = sys.stdin.read().split()
    n, s = int(data[0]), data[1].strip()
    print(calc_red_weight(s))
Java
import java.io.*;
import java.util.*;
public class Main {
    static long solve(String s) {
        int n = s.length();
        long[] preE = new long[n];
        for (int i = 0; i < n; i++) {
            preE[i] = (s.charAt(i) == 'e' ? 1 : 0) + (i > 0 ? preE[i-1] : 0);
        }
        long cntR = 0, sumI = 0, sumPE = 0, sumIPI = 0;
        long ans = 0;
        for (int k = 0; k < n; k++) {
            char c = s.charAt(k);
            if (c == 'r') {
                cntR++;
                sumI += k;
                sumPE += preE[k];
                sumIPI += k * preE[k];
            } else if (c == 'd') {
                if (cntR == 0) continue;
                long E = k > 0 ? preE[k-1] : 0;
                long M = cntR, A = sumI, B = sumPE, C = sumIPI;
                long contrib = E * (M * k - A) - (k * B - C);
                ans += 2 * contrib;
            }
        }
        return ans;
    }
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine().trim());
        String s = br.readLine().trim();
        System.out.println(solve(s));
    }
}
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    string s;
    cin >> n >> s;
    vector<long long> preE(n);
    for (int i = 0; i < n; i++) {
        preE[i] = (s[i] == 'e') + (i > 0 ? preE[i-1] : 0);
    }
    long long cntR = 0, sumI = 0, sumPE = 0, sumIPI = 0;
    long long ans = 0;
    for (int k = 0; k < n; k++) {
        if (s[k] == 'r') {
            cntR++;
            sumI += k;
            sumPE += preE[k];
            sumIPI += (long long)k * preE[k];
        } else if (s[k] == 'd') {
            if (cntR == 0) continue;
            long long E = k > 0 ? preE[k-1] : 0;
            long long M = cntR, A = sumI, B = sumPE, C = sumIPI;
            long long contrib = E * (M * k - A) - (k * B - C);
            ans += 2 * contrib;
        }
    }
    cout << ans << "\n";
    return 0;
}
        题目内容
小红有一个长度为n的字符串s=s1s2⋅⋅.sn,她定义长度为3的子序列sisjsk的权值为∣i−j∣+∣i−k∣+∣j−k∣。
现在,小红希望你计算所有"red"子序列的权值之和。
如果字符串t="red"可以通过删除字符串s中的若干
(可能为零或全部)元素得到,则字符串t是字符串s的"red"子序列。
输入描述
第一行输入一个整数n(1≤n≤2×105),表示字符串的长度。
第二行输入一个长度为n,且只由'r'、'e'、'd'三个小写字母组成的字符串
输出描述
在一行上输出一个整数,代表所有"red"子序列的权值之和。
样例1
输入
4
reed
输出
12
说明
在该样例中,一共有两个不同的"red"子序列:
删除第二个字符得到的子序列S1S3S4权值为
∣1−3∣+∣1−4∣+∣3−4∣=6;
删除第三个字符得到的子序列S1S2S4权值为
∣1−2∣+∣1−4∣+∣2−4∣=6。
样例2
输入
7
redeeed
输出
52