易中 · 字符串

字符串压缩

用单个指针扫整行:站在哪个字符上,就数它连着出现几次,连字符带计数一起打印 —— 整段就过去了。不需要对字符串套双重循环,计数器本身就是那个内层循环。

一句话答案一个下标走到底:每个位置数出连续相同字符的段长,打印字符和计数(永远打印,1 也不例外 —— a10 合法),然后跳到段后继续。

题目

输入: 一行 1 到 200 个小写字母,不含空格。

输出: 游程编码:每个极大连续段输出「字符 + 长度」。长度永远打印,1 也不例外。

样例:

输入输出
aaabbca3b2c1
aa1

计数可以到两位数:连续 10 个相同字母输出 a10,10 个字符的段只占两个输出字符 —— 这正是压缩的全部意义。

你的解答

main.cpp — 你的解答
「运行我的输入」用的自定义输入

判题在浏览器本地运行。死循环会在 3 秒后被自动中断 —— 但还是确保每个循环都有出口。代码随打随存。

卡住了?提示

提示 1 — 段会自己结束

站在位置 i 上,只要 s[i] == c 就数下去。计数循环退出时,i 已经指着下一段的第一个字符 —— 不用跳、不用回头,要重置的只有计数器。单指针贯穿全串,永不后退。

提示 2 — 计数按数字打印

cout << count 一位、两位、三位数都处理 —— 不需要手动拆位,那是二进制表示那题的活。但注意必须做到的:计数为 1 也要打印。对 abc 输出 abc 看着自然,其实是错的。

提示 3 — 读整行

cin.getline 这课再绕一圈:这里虽然没有空格,但对「必须整个拿走的一行」来说它仍是对的读法 —— 而且不会把回车留在缓冲区毒害后面的读取。完整论证在 反转单词顺序 里。

题解(先自己试试!)
#include <iostream>
using namespace std;

int main() {
    char s[205];
    cin.getline(s, 205);

    int i = 0;
    while (s[i] != '\0') {
        char c = s[i];
        int count = 0;
        while (s[i] == c) {
            count++;
            i++;
        }
        cout << c << count;
    }
    cout << endl;
    return 0;
}

游程编码是真实压缩技术的祖先(传真、JPEG、PNG 的管线里都有它),在这里它同时也是那个「状态机式」字符串模式:一个标志、一个计数器、无需前瞻。它的计数表亲 —— 数「某一类」的连续段而不是「某个字符」的段 —— 是 数单词。

深度解析

为什么这样做对

单个前进指针 + 会自动终止的内层计数:s[i] 一旦不等于 c,i 已经站在下一段开头。没有第二下标、没有前瞻、没有第二遍 —— 计数器就是内层循环,字符串自己完成分段。

代价

行长 L 时 O(L) 时间 —— 外层指针让每个字符恰好被访问一次 —— 输出 O(L),本题里输出永不长于输入。

常见错法

  • 计数为 1 时省略不印,感觉更整洁,其实是错的:题面要求永远打印,'a' 必须输出成 a1。
  • 用 cin >> 读会在空格处停(这里没有空格,但习惯要紧)—— 更重要的是,getline 才是这个格式假定的「整行读法」。
  • 计数和下一个字符之间不需要分隔符 —— 格式就是字符紧跟数字,没有空格。

下一步

同样的「一标志一计数器」扫描,数的是某一*类*(空格)的连续段而不是某个字符的段 —— 那是 数单词。

记住这两句

  • 内层计数循环就是分段本身 —— i 免费落在下一段开头。
  • 计数永远打印,1 也不例外;多位数交给 cout。
🐞 这段是错的 —— 点开,亲手让它挂一次

计数为 1 时就不印了 —— 判题要的是 a1,不是 a

#include <iostream>
using namespace std;

int main() {
    char s[205];
    cin.getline(s, 205);
    int i = 0;
    while (s[i] != '\0') {
        char c = s[i];
        int count = 0;
        while (s[i] == c) {
            count++;
            i++;
        }
        cout << c;
        if (count > 1) cout << count;   // bug: count of 1 is required too
    }
    cout << endl;
    return 0;
}