题解:学而思编程 优秀的排列
【题目来源】
学而思编程:优秀的排列
【题目描述】
给定一个字符串 \(s\),\(s\) 中只包含字符 \(0\) 和 \(1\),如果字符串 \(s\) 满足:
- 不存在两个相邻的 \(1\);
- 每个 \(0\) 两边至少有一个 \(1\)。
则说明字符串 \(s\) 是一个优秀的排列。
【输入】
第一行包含整数 \(T\),表示共有 \(T\) 组测试数据。
每组数据:一行一个字符串 \(s\)。
【输出】
每组数据输出一行结果,如果字符串 \(s\) 是优秀的排列,输出 Yes,否则输出 No。
【输入样例】
3
101
1011
10001
【输出样例】
Yes
No
No
【核心思想】
-
问题分析:给定只含
0和1的字符串 \(s\),判断是否满足两个条件:(1) 不存在相邻的1;(2) 每个0两边至少有一个1。这是一个字符串模拟 + 约束验证问题,关键在于逐字符检查两个条件的满足情况。 -
算法选择:
- 条件1直接检查:遍历字符串,若发现
s[i] == '1'且s[i+1] == '1'则不合法 - 条件2边界处理:每个
0的左右相邻位置(存在时)必须至少有一个1。等价于禁止00子串出现在边界,以及禁止000子串
- 条件1直接检查:遍历字符串,若发现
-
关键步骤:
- 初始化:读取 \(T\)(测试组数)
- 处理每组数据:
- 读取字符串 \(s\),在开头添加
'0'作为哨兵(简化边界判断) flag = true:合法性标记- 遍历 \(i\) 从 \(1\) 到
s.size() - 1(原字符串位置):- 若
s[i] == '1':- 若前一个字符也是
'1':flag = false,break(条件1违反) - 否则:将该
'1'加入处理序列
- 若前一个字符也是
- 若
s[i] == '0':- 若前一个字符是
'0':- 若
i是最后一个字符:flag = false,break(末尾0无右侧1) - 若下一个字符也是
'0':flag = false,break(000中中间0两侧无1)
- 若
- 否则(前一个字符是
'1'):将该'0'加入处理序列
- 若前一个字符是
- 若
- 若
flag为真:输出Yes,否则输出No
- 读取字符串 \(s\),在开头添加
-
时间/空间复杂度:
- 时间复杂度:\(O(T \times |s|)\),每组数据线性扫描
- 空间复杂度:\(O(|s|)\),存储字符串和处理序列
-
约束验证的核心思想:
- 条件1的局部性:相邻
1只需检查相邻两个字符,\(O(1)\) 判定 - 条件2的等价转化:"每个
0两边至少有一个1" 等价于:- 字符串不能以
0开头(左侧无字符,无法有1) - 字符串不能以
0结尾(右侧无字符,无法有1) - 不能出现
000(中间0两侧都是0) - 不能出现
00在边界(边界0只有一侧)
- 字符串不能以
- 哨兵简化:开头添加
'0'统一处理逻辑,但需注意不影响原字符串判断 - 贪心判定:两个条件均为局部约束,逐字符检查即可确定全局合法性,无需回溯
- 适用于"二进制字符串模式约束"类问题,通过局部条件检查实现全局验证
- 条件1的局部性:相邻
【算法标签】
字符串入门
【代码详解】
#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
浙公网安备 33010602011771号