题解:学而思编程 真空扫地机器人

【题目来源】

学而思编程:真空扫地机器人

【题目描述】

Imp家里有一个扫地机器人,他还养了一条狗。扫地机器人工作时会发出很多噪音,狗很害怕这些噪音。

有一天,Imp提取了机器人的运行记录,这些记录是 \(n\) 个字符串 \(t_1,t_2,...,t_n\),每个字符串只包含英文字母sh。但是他无法知道这些记录的时间先后顺序,他将这些字符串连接成一个字符串 \(T\),使 \(T\) 中子序列sh的个数最多。

噪音的数量定义为:字符串中子序列sh的个数。准确地说,是满足 \(i<j\)\(T_i=s,T_j=h\)\((i,j)\) 个数。

【输入】

\(1\) 行,\(1\) 个正整数 \(n\)

接下来 \(n\) 行,每行 \(1\) 个字符串 \(t_i\)

【输出】

输出 \(1\) 个整数,为最大的噪音数量。

【输入样例】

4
ssh
hs
s
hhhs

【输出样例】

18

【核心思想】

  1. 问题分析:给定 \(n\) 个字符串,每个字符串只包含 's' 和 'h'。需要将这些字符串按某种顺序拼接,使得拼接后的字符串中 "sh" 子序列(即前面的 's' 和后面的 'h' 组成的对)的总数最大化。这是一个自定义排序贪心 + 前缀和统计问题,关键在于找到最优的字符串拼接顺序。

  2. 算法选择

    • 自定义排序规则:对于两个字符串 \(a\)\(b\),如果 \(a\) 放在 \(b\) 前面产生的 "sh" 对数更多,则 \(a\) 排在前面
    • 排序规则推导:设字符串 \(a\)\(s_a\) 个 's' 和 \(h_a\) 个 'h',字符串 \(b\)\(s_b\) 个 's' 和 \(h_b\) 个 'h'
      • \(a\) 放前面时贡献:\(s_a \times h_b\)\(a\) 中的 's' 与 \(b\) 中的 'h' 配对)
      • \(b\) 放前面时贡献:\(s_b \times h_a\)\(b\) 中的 's' 与 \(a\) 中的 'h' 配对)
      • 比较:若 \(s_a \times h_b > s_b \times h_a\),则 \(a\) 应排在 \(b\) 前面
    • 前缀和统计:拼接后遍历字符串,维护前缀 's' 的数量,遇到 'h' 时将当前前缀 's' 的数量累加到答案
  3. 关键步骤

    • 读取输入\(n\)(字符串数量)、\(n\) 个字符串 \(t_i\)
    • 统计字符:对每个字符串统计 's' 的数量 \(s_i\) 和 'h' 的数量 \(h_i\)
    • 自定义排序:按 \(s_i \times h_j > s_j \times h_i\) 的规则排序
    • 拼接字符串:按排序后的顺序拼接所有字符串
    • 前缀和统计(遍历拼接后的字符串):
      • 遇到 's':sa[i] = sa[i-1] + 1(更新前缀 's' 的数量)
      • 遇到 'h':sa[i] = sa[i-1]ans += sa[i](累加当前 'h' 前面的 's' 总数)
    • 输出结果:最大 "sh" 子序列对数 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \times L \log n)\)\(L\) 为字符串平均长度,主要是排序和统计的复杂度
    • 空间复杂度:\(O(n \times L)\),用于存储字符串
  5. 自定义排序贪心 + 前缀和的核心思想

    • 交换论证:通过比较相邻两个字符串交换位置后的贡献,推导出最优排序规则
    • 排序规则:按 \(s \times h\) 的交叉乘积比较,确保产生更多 "sh" 对的字符串排在前面
    • 前缀和优化:拼接后只需一次线性扫描,用前缀和维护 's' 的数量,高效统计 "sh" 对数
    • 贪心正确性:自定义排序确保任意相邻两个字符串的顺序都是最优的,从而整体顺序最优
    • 适用于字符串拼接优化、顺序相关最大化问题、子序列统计类问题

【算法标签】

贪心

【代码详解】

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e5 + 5;

int n, sa[N], ans;  // sa: 前缀s的个数, ans: 最终答案
struct node
{
    int s, h;  // s: 字符串中's'的数量, h: 字符串中'h'的数量
    string c;  // 原始字符串
} a[N];

// 自定义排序规则:比较s1/h1和s2/h2的大小
bool cmp(node x, node y)
{
    return x.s * y.h > y.s * x.h;
}

signed main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i].c;
        // 统计每个字符串中's'和'h'的数量
        for (int j = 0; j < a[i].c.size(); j++)
        {
            if (a[i].c[j] == 's')
            {
                a[i].s++;
            }
            else
            {
                a[i].h++;
            }
        }
    }
    sort(a + 1, a + n + 1, cmp);  // 按规则排序

    // 将排序后的字符串拼接
    string s = " ";
    for (int i = 1; i <= n; i++)
    {
        s += a[i].c;
    }
    // 计算答案:对于每个'h',统计它前面的's'个数
    for (int i = 1; i < s.size(); i++)
    {
        if (s[i] == 's')
        {
            sa[i] = sa[i - 1] + 1;  // 更新前缀's'的数量
        }
        else
        {
            sa[i] = sa[i - 1];  // 遇到'h'时不增加计数
            ans += sa[i];       // 累加当前'h'前面的's'总数
        }
    }
    cout << ans;

    return 0;
}

【运行结果】

4
ssh
hs
s
hhhs
18
posted @ 2026-06-14 10:47  团爸讲算法  阅读(4)  评论(0)    收藏  举报