题解:学而思编程 优秀的排列

【题目来源】

学而思编程:优秀的排列

【题目描述】

给定一个字符串 \(s\)\(s\) 中只包含字符 \(0\)\(1\),如果字符串 \(s\) 满足:

  1. 不存在两个相邻的 \(1\)
  2. 每个 \(0\) 两边至少有一个 \(1\)

则说明字符串 \(s\) 是一个优秀的排列。

【输入】

第一行包含整数 \(T\),表示共有 \(T\) 组测试数据。

每组数据:一行一个字符串 \(s\)

【输出】

每组数据输出一行结果,如果字符串 \(s\) 是优秀的排列,输出 Yes,否则输出 No。

【输入样例】

3
101
1011
10001

【输出样例】

Yes
No
No

【核心思想】

  1. 问题分析:给定只含 01 的字符串 \(s\),判断是否满足两个条件:(1) 不存在相邻的 1;(2) 每个 0 两边至少有一个 1。这是一个字符串模拟 + 约束验证问题,关键在于逐字符检查两个条件的满足情况。

  2. 算法选择

    • 条件1直接检查:遍历字符串,若发现 s[i] == '1's[i+1] == '1' 则不合法
    • 条件2边界处理:每个 0 的左右相邻位置(存在时)必须至少有一个 1。等价于禁止 00 子串出现在边界,以及禁止 000 子串
  3. 关键步骤

    • 初始化:读取 \(T\)(测试组数)
    • 处理每组数据
      • 读取字符串 \(s\),在开头添加 '0' 作为哨兵(简化边界判断)
      • flag = true:合法性标记
      • 遍历 \(i\)\(1\)s.size() - 1(原字符串位置):
        • s[i] == '1'
          • 若前一个字符也是 '1'flag = falsebreak(条件1违反)
          • 否则:将该 '1' 加入处理序列
        • s[i] == '0'
          • 若前一个字符是 '0'
            • i 是最后一个字符:flag = falsebreak(末尾 0 无右侧 1
            • 若下一个字符也是 '0'flag = falsebreak000 中中间 0 两侧无 1
          • 否则(前一个字符是 '1'):将该 '0' 加入处理序列
      • flag 为真:输出 Yes,否则输出 No
  4. 时间/空间复杂度

    • 时间复杂度:\(O(T \times |s|)\),每组数据线性扫描
    • 空间复杂度:\(O(|s|)\),存储字符串和处理序列
  5. 约束验证的核心思想

    • 条件1的局部性:相邻 1 只需检查相邻两个字符,\(O(1)\) 判定
    • 条件2的等价转化:"每个 0 两边至少有一个 1" 等价于:
      • 字符串不能以 0 开头(左侧无字符,无法有 1
      • 字符串不能以 0 结尾(右侧无字符,无法有 1
      • 不能出现 000(中间 0 两侧都是 0
      • 不能出现 00 在边界(边界 0 只有一侧)
    • 哨兵简化:开头添加 '0' 统一处理逻辑,但需注意不影响原字符串判断
    • 贪心判定:两个条件均为局部约束,逐字符检查即可确定全局合法性,无需回溯
    • 适用于"二进制字符串模式约束"类问题,通过局部条件检查实现全局验证

【算法标签】

字符串入门

【代码详解】

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

int main()
{
    int T;  // 测试用例数量
    cin >> T;

    for (int i = 0; i < T; i++)  // 处理每个测试用例
    {
        string s;
        cin >> s;
        s = "0" + s;  // 在字符串前添加字符'0'

        vector<char> a;  // 用于存储处理过的字符
        bool flag = true;  // 标记字符串是否合法

        for (int i = 0; i < s.size(); i++)  // 遍历字符串
        {
            if (s[i] == '1')  // 当前字符是'1'
            {
                if (a.empty())  // 如果a为空
                {
                    a.push_back(s[i]);  // 直接加入
                }
                else if (a[a.size() - 1] == '0')  // 如果前一个字符是'0'
                {
                    a.push_back(s[i]);  // 可以加入
                }
                else  // 前一个字符也是'1'(连续两个'1')
                {
                    flag = false;  // 非法
                    break;
                }
            }
            else  // 当前字符是'0'
            {
                if (a.empty())  // 如果a为空
                {
                    a.push_back(s[i]);  // 直接加入
                }
                else if (a[a.size() - 1] == '0')  // 前一个字符是'0'
                {
                    if (i == s.size() - 1)  // 如果是最后一个字符
                    {
                        flag = false;  // 非法
                        break;
                    }
                    else
                    {
                        if (s[i + 1] == '0')  // 后面跟着'0'
                        {
                            flag = false;  // 非法
                            break;
                        }
                    }
                }
                else  // 前一个字符是'1'
                {
                    a.push_back(s[i]);  // 可以加入
                }
            }
        }

        if (flag)
        {
            cout << "Yes" << endl;
        }
        else
        {
            cout << "No" << endl;
        }
    }

    return 0;
}

【运行结果】

3
101
Yes
1011
No
10001
No
posted @ 2026-07-04 14:58  团爸讲算法  阅读(5)  评论(0)    收藏  举报