字符串中找出连续最长的数字串、岛屿数量、拼三角
文章目录
1. 字符串中找出连续最长的数字串
🔗 题目链接 | 难度:🟢 Easy
问题描述
text
读入一个字符串 str,输出该字符串中连续最长的数字串。
输入描述:
一个测试输入包含 1 个测试用例,一个字符串 str,长度不超过 255。
输出描述:
在一行内输出 str 中连续最长的数字串。
示例:
输入:abcd12345ed125ss123456789
输出:123456789
题目解法
解法一:
思路:
滑动数组,不多赘述
Bug: 代码审查 --- 边界条件
- 问题 :当输入字符串不含任何数字时,
resl=resr=0、r-l与resr-resl均为 0,最终substr(0, 1)会输出首字符而非空串,属于边界遗漏。- 建议 :初始化
resr = -1使resr - resl + 1 = 0时输出空串;或在最后添加特判if (resr < resl)直接返回。
复杂度分析:时间复杂度 O(n),空间复杂度 O(1)。一次线性遍历,仅用常数变量维护窗口起止位置。
cpp
int main() {
int resl = 0, resr = 0;
int l = 0, r = 0;
string str;
cin >> str;
bool is_num = false;
for(int i = 0; i < str.size(); ++i)
{
if(str[i] >= '0' && str[i] <= '9')
{
if(is_num)
r++;
else
{
//处理上次的数组
if(r - l > resr - resl)
{
resl = l;
resr = r;
}
l = r = i;
is_num = true;
}
}
else
is_num = false;
}
//处理上次的数组
if(r - l > resr - resl)
{
resl = l;
resr = r;
}
cout << str.substr(resl, resr - resl + 1);
}
错误记录
无
要点总结
- 滑动窗口 / 双指针 :维护
[l, r]区间标记当前连续数字串,遇到非数字时暂存、遇到新数字串时比较长度更新最优解。 - 字符判断 :
c >= '0' && c <= '9'是判断数字字符的可移植写法,也可用isdigit()标准库函数。 - 边界处理:遍历结束后需额外处理最后一组数字串;注意输入不含数字时的空串输出。
- 空间效率:原地 O(1) 空间扫描是最优方案,无需额外数组。
2. 岛屿数量
🔗 题目链接 | 难度:🟡 Medium
问题描述
text
给定一个 m × n 的二维网格,其中 '1' 表示陆地,'0' 表示海洋。
如果两个 '1' 相邻(上下左右四个方向,不包含斜对角),则它们属于同一个岛屿。
请计算网格中岛屿的总数量。
数据范围:1 ≤ n, m ≤ 200
进阶:空间复杂度 O(nm),时间复杂度 O(nm)
示例 1:
输入:
[
[1,1,0],
[1,1,0],
[0,0,1]
]
输出:2
示例 2:
输入:
[
[1,0,1],
[0,1,0],
[1,0,1]
]
输出:5
题目解法
解法一:
思路:
思路说明 :
BFS 洪水填充。遍历网格每个格子,遇到 '1' 即计数 +1,并以该点为起点进行 BFS:将相连的所有 '1' 标记为 '0'(沉岛),完成一个岛屿的统计。重复直至所有格子遍历完毕。
复杂度分析:时间 O(m×n)(每个格子访问一次),空间 O(min(m, n))(BFS 队列最大长度)。
cpp
class Solution {
public:
using PII = pair<int, int>;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int solve(vector<vector<char> >& grid) {
int xsize = grid.size();
if(!xsize) return 0;
int ysize = grid[0].size();
if(!ysize) return 0;
int count = 0;
for(int sx = 0; sx < xsize; ++sx)
{
for(int sy = 0; sy < ysize; ++sy)
{
if(grid[sx][sy] == '0')
continue;
count++;
queue<PII> q;
q.push({sx, sy});
grid[sx][sy] = '0';
while(!q.empty())
{
int x = q.front().first;
int y = q.front().second;
q.pop();
for(int i = 0; i < 4; ++i)
{
int m = x + dx[i];
int n = y + dy[i];
if(m >= 0 && m < xsize && n >=0 && n < ysize && grid[m][n] == '1')
{
q.push({m, n});
grid[m][n] = '0';
}
}
}
}
}
return count;
}
};
解法二:DFS(深度优先搜索)
思路:
使用递归替代队列进行深度优先搜索,代码更简洁。同样采用"沉岛"策略,将已访问陆地标记为 '0'。
cpp
class Solution {
public:
void dfs(vector<vector<char>>& grid, int i, int j) {
if (i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size() || grid[i][j] == '0')
return;
grid[i][j] = '0'; // 沉岛
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
int solve(vector<vector<char>>& grid) {
if (grid.empty() || grid[0].empty()) return 0;
int count = 0;
for (int i = 0; i < grid.size(); ++i) {
for (int j = 0; j < grid[0].size(); ++j) {
if (grid[i][j] == '1') {
++count;
dfs(grid, i, j);
}
}
}
return count;
}
};
复杂度分析:时间 O(m×n),空间 O(m×n)(最坏递归栈深度为整个网格)。
错误记录
无
要点总结
- FloodFill 算法:从一个种子点出发,通过 BFS/DFS 遍历所有连通节点,标记访问("沉岛")避免重复计数。
- BFS vs DFS:BFS 用队列实现,无栈溢出风险;DFS 代码更简洁。本题两种均可。
- 方向数组 :
dx[4] = {0,0,1,-1}, dy[4] = {1,-1,0,0}简化四个方向的遍历。 - 原地标记 :将已访问的
'1'修改为'0',无需额外 visited 数组(前提是允许修改原数组)。
3. 拼三角
🔗 题目链接 | 难度:🟢 Easy
问题描述
text
给出 6 根棍子,每根棍子有长度。问是否能取其中的 3 根拼成一个三角形,
并且剩下的 3 根也能拼成一个三角形?
输入描述:
第一行一个整数 T,表示数据组数(1 ≤ T ≤ 10)。
对于每组数据,输入一行 6 个整数,表示每根棍子的长度(1 ≤ 长度 ≤ 100)。
输出描述:
对于每组数据,如果可以拼成两个三角形,输出 "Yes",否则输出 "No"。
示例:
输入:
2
1 1 1 1 1 1
1 2 3 4 5 6
输出:
Yes
No
题目解法
解法一:
思路:
思路说明 :暴力枚举。三层循环 i < j < k 枚举所有 C(6,3) = 20 种取 3 根棍子的组合,验证是否能构成三角形。若能,再验证剩余 3 根是否能构成三角形。一旦找到合法分组即返回 true。
复杂度分析:时间 O(20) ≈ O(1)(常数次枚举),空间 O(1)。6 根棍子规模极小,暴力枚举即可。
cpp
bool fun(vector<int>& bar)
{
for(int i = 0; i < 4; ++i)
{
for(int j = i + 1; j < 5; j++)
{
for(int k = j + 1; k < 6; ++k)
{
if(bar[k] + bar[j] > bar[i] &&
bar[i] + bar[k] > bar[j] &&
bar[i] + bar[j] > bar[k]
)
{
vector<int> bar2;
for(int l = 0; l < 6; ++l)
{
if(l != i && l != j && l != k)
bar2.push_back(bar[l]);
}
if(bar2[1] + bar2[2] > bar2[0] &&
bar2[0] + bar2[2] > bar2[1] &&
bar2[0] + bar2[1] > bar2[2]
)
return true;
}
}
}
}
return false;
}
int main() {
int n;
cin >> n;
while(n--)
{
vector<int> bar;
for(int i = 0; i < 6; ++i)
{
int x;
cin >> x;
bar.push_back(x);
}
if(fun(bar))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
解法二:排序 + 4 种固定分组
思路:
先将 6 根棍子排序。观察发现,将排序后的 6 根棍子分成两组(每组 3 根),所有可能的分组方式可以归纳为 4 种固定模式。只需检查这 4 种分组是否各能构成三角形即可,无需暴力枚举所有组合。
cpp
bool canFormTriangle(int a, int b, int c) {
// 排序后,只需检查 a + b > c(c 为最大边)
return a + b > c;
}
bool fun(vector<int>& bar) {
sort(bar.begin(), bar.end());
// 检查 4 种固定分组(排序后归纳可得,覆盖所有可能)
// 组合1: (0,1,2) + (3,4,5)
if (canFormTriangle(bar[0], bar[1], bar[2]) &&
canFormTriangle(bar[3], bar[4], bar[5]))
return true;
// 组合2: (0,2,3) + (1,4,5)
if (canFormTriangle(bar[0], bar[2], bar[3]) &&
canFormTriangle(bar[1], bar[4], bar[5]))
return true;
// 组合3: (0,3,4) + (1,2,5)
if (canFormTriangle(bar[0], bar[3], bar[4]) &&
canFormTriangle(bar[1], bar[2], bar[5]))
return true;
// 组合4: (0,4,5) + (1,2,3)
if (canFormTriangle(bar[0], bar[4], bar[5]) &&
canFormTriangle(bar[1], bar[2], bar[3]))
return true;
return false;
}
复杂度分析 :时间 O(1)(常数次比较),空间 O(1)。排序 O(6 log 6) 可视为常数。
相较于暴力枚举 20 种组合的 O(20) 时间,两者均为常数级,但排序法代码更简洁,思路更清晰。
错误记录
无
要点总结
- 暴力枚举:6 根棍子选 3 根,仅 C(6,3) = 20 种组合,暴力枚举即可,无需复杂算法。
- 三角形判定:排序后只需判断最短两边之和 > 最长边,全排列的三个不等式可简化为一个。
- 枚举技巧 :三层循环
i < j < k保证不重不漏;剩余 3 根棍子同样验证三角形条件。