题解:洛谷 P1098 字符串的展开

【题目来源】

洛谷:P1098 [NOIP 2007 提高组] 字符串的展开 - 洛谷(luogu.com.cn)

【题目描述】

在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于 d-h 或者 4-8 的字串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为 defgh45678。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:

(1)遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号 - ,减号两侧同为小写字母或同为数字,且按照 ASCII 码的顺序,减号右边的字符严格大于左边的字符。

(2)参数 \(p_1\):展开方式。\(p_1=1\) 时,对于字母子串,填充小写字母;\(p_1=2\) 时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。\(p_1=3\) 时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号 * 来填充。

(3)参数 \(p_2\):填充字符的重复个数。\(p_2=k\) 表示同一个字符要连续填充 \(k\) 个。例如,当 \(p_2=3\) 时,子串 d-h 应扩展为 deeefffgggh。减号两边的字符不变。

(4)参数 \(p_3\):是否改为逆序:\(p_3=1\) 表示维持原来顺序,\(p_3=2\) 表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当 \(p_1=1\)\(p_2=2\)\(p_3=2\) 时,子串 d-h 应扩展为 dggffeeh

(5)如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:d-e 应输出为 de3-4 应输出为 34。如果减号右边的字符按照 ASCII 码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:d-d 应输出为 d-d3-1 应输出为 3-1

【输入】

共两行。

\(1\) 行为用空格隔开的 \(3\) 个正整数,依次表示参数 \(p_1,p_2,p_3\)

\(2\) 行为一行字符串,仅由数字、小写字母和减号 - 组成。行首和行末均无空格。

【输出】

共一行,为展开后的字符串。

【输入样例】

1 2 1
abcs-w1234-9s-4zz

【输出样例】

abcsttuuvvw1234556677889s-4zz

【核心思想】

  1. 问题分析:给定一个包含数字、小写字母和减号的字符串,根据三个参数 \(p_1, p_2, p_3\) 展开形如 a-b1-9 的子串。展开规则包括:字符类型判定(同为数字或小写字母)、填充字符大小写/星号、重复次数、正序/逆序。本质是一个字符串解析与模拟问题,需要根据规则分情况处理每个 - 及其两侧字符。

  2. 算法选择

    • 遍历 + 分类处理:遍历字符串,当遇到 - 时,检查其左右字符是否满足展开条件(同为数字或同为字母,且左 < 右)。若满足,则根据 \(p_1, p_2, p_3\) 生成填充字符串;若不满足(如左 >= 右或类型不同),则原样保留 -
    • 填充生成:使用循环从 left+1right-1,根据 \(p_3\) 决定正序或逆序,根据 \(p_1\) 决定输出小写、大写或 *,每个字符重复 \(p_2\) 次。
  3. 关键步骤

    • 读入:读取 \(p_1, p_2, p_3\) 和字符串 \(s\)
    • 遍历i 从 0 到 s.size()-1
      • s[i] == '-'i > 0i < s.size()-1
        • 左字符 L = s[i-1],右字符 R = s[i+1]
        • R - L == 1,则跳过(只删除减号)。
        • 否则若 L >= R!judge(L, R),则输出 '-'
        • 否则调用 expand(L, R) 生成填充序列。
      • 否则输出 s[i]
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot p_2 \cdot |R-L|)\),但实际展开长度有限。
    • 空间复杂度:\(O(1)\)
  5. 字符串展开与条件分支

    • 类型判定isdigitisalpha 用于判断字符类型。
    • 顺序与逆序\(p_3=1\) 时正序输出,\(p_3=2\) 时逆序输出。
    • 填充字符\(p_1=1\) 小写,\(p_1=2\) 大写,\(p_1=3\) 星号。
    • 边界情况:当 R - L == 1 时直接跳过 -,不输出填充。
    • 适用场景:这类“字符串展开”问题是模拟题中较为复杂的类型,考查字符串处理和条件逻辑。

【算法标签】

普及- #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int p1, p2, p3; // p1: 控制大小写或替换为*;p2: 控制填充字符的重复次数;p3: 控制填充顺序(正序或逆序)
string s;       // 输入的字符串

// 判断两个字符是否同时为数字或同时为字母
bool judge(char a, char b)
{
    if (isdigit(a) && isdigit(b))  // 如果a和b都是数字
        return true;
    else if (isalpha(a) && isalpha(b))  // 如果a和b都是字母
        return true;
    return false; // 否则返回false
}

// 扩展填充函数,根据p1、p2、p3的规则填充字符
void exp(char a, char b)
{
    if (p3 == 1) { // 如果p3为1,按正序填充
        for (char i = a + 1; i < b; i++)
		{ // 从a的下一个字符到b的前一个字符
            for (int j = 0; j < p2; j++)
			{ // 重复p2次
                if (p1 == 1) printf("%c", tolower(i)); // p1为1,输出小写字母
                else if (p1 == 2) printf("%c", toupper(i)); // p1为2,输出大写字母
                else printf("*"); // p1为3,输出*
            }
        }
    }
	else
	{ // 如果p3为2,按逆序填充
        for (char i = b - 1; i > a; i--)
		{ // 从b的前一个字符到a的下一个字符
            for (int j = 0; j < p2; j++)
			{ // 重复p2次
                if (p1 == 1) printf("%c", tolower(i)); // p1为1,输出小写字母
                else if (p1 == 2) printf("%c", toupper(i)); // p1为2,输出大写字母
                else printf("*"); // p1为3,输出*
            }
        }
    }
}

int main()
{
    cin >> p1 >> p2 >> p3 >> s; // 输入p1、p2、p3和字符串s
    for (int i = 0; i < s.size(); i++)
	{ // 遍历字符串s
        if (s[i] == '-')
		{ // 如果当前字符是'-'
            if (s[i + 1] - s[i - 1] == 1)
			{ // 如果'-'前后的字符是连续的(如a-b)
                continue; // 跳过,不处理
            }
			else if (s[i + 1] <= s[i - 1])
			{ // 如果'-'后的字符小于等于前面的字符(如b-a或a-a)
                cout << s[i]; // 直接输出'-'
                continue;
            }
			else if (judge(s[i - 1], s[i + 1]))
			{ // 如果'-'前后的字符同时为数字或字母
                exp(s[i - 1], s[i + 1]); // 调用exp函数进行填充
                continue;
            }
        }
        cout << s[i]; // 输出当前字符
    }
    return 0;
}

【运行结果】

1 2 1
abcs-w1234-9s-4zz
abcsttuuvvw1234556677889s-4zz
posted @ 2026-08-31 09:23  团爸讲算法  阅读(8)  评论(0)    收藏  举报