110. 字符串接龙
题目链接:110. 字符串接龙
题目描述:
字典 strList 中从字符串 beginStr 和 endStr 的转换序列是一个按下述规格形成的序列:
- 序列中第一个字符串是 beginStr。
- 序列中最后一个字符串是 endStr。
- 每次转换只能改变一个字符。
- 转换过程中的中间字符串必须是字典 strList 中的字符串,且strList里的每个字符串只用使用一次。
给你两个字符串 beginStr 和 endStr 和一个字典 strList,找到从 beginStr 到 endStr 的最短转换序列中的字符串数目。如果不存在这样的转换序列,返回 0。
解题思路:
这道题要解决两个问题:
- 图中的线是如何连在一起的
- 起点和终点的最短路径长度
1)判断点与点之间的关系,需要判断是不是差一个字符,如果差一个字符,那就是有链接。
2)求起点和终点的最短路径长度,这里无向图求最短路,广搜最为合适,广搜只要搜到了终点,那么一定是最短的路径。因为广搜就是以起点中心向四周扩散的搜索。
另外需要有一个注意点:
- 本题是一个无向图,需要用标记位,标记着节点是否走过,否则就会死循环!
- 使用set来检查字符串是否出现在字符串集合里更快一些
代码:
#include<iostream>
#include<unordered_set>
#include<queue>
#include<unordered_map>
#include<string>
using namespace std;
int main()
{
int n;
cin >> n;
unordered_set<string> strList;
string beginStr, endStr;
cin >> beginStr >> endStr;
for(int i = 0; i < n; i++)
{
string s;
cin >> s;
strList.insert(s);
}
unordered_map<string,int> visitStr;
queue<string> q;
q.push(beginStr);
visitStr.insert({beginStr,1});
while(!q.empty())
{
string val = q.front();
q.pop();
int path = visitStr[val];
for(int i = 0; i < val.size(); i++)
{
string newStr = val;
for(int j = 0; j < 26; j++)
{
newStr[i] = j + 'a';
if(newStr == endStr)
{
cout << path + 1 << endl;
return 0;
}
if(visitStr.find(newStr) == visitStr.end() && strList.find(newStr) != strList.end())
{
visitStr[newStr] = path + 1;
q.push(newStr);
}
}
}
}
cout << 0 << endl;
return 0;
}
105.有向图的完全可达性
题目链接:105. 有向图的完全可达性
题目描述:
给定一个有向图,包含 N 个节点,节点编号分别为 1,2,...,N。现从 1 号节点开始,如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。
解题思路:
使用数组visited来保存当前节点的访问情况,使用dfs来遍历所有节点。最后如果所有节点均被访问,则输出1。
代码:
#include<iostream>
#include<vector>
#include<list>
#include<algorithm>
using namespace std;
void dfs(vector<list<int>>& graph,vector<bool>& visited,int key)
{
if(visited[key]) return;
visited[key] = true;
for(auto x : graph[key])
dfs(graph,visited,x);
}
int main()
{
int n,k;
cin >> n >> k;
vector<list<int>> graph(n+1);
for(int i = 0; i < k ; i++)
{
int s,t;
cin >> s >> t;
graph[s].push_back(t);
}
vector<bool> visited(n+1);
dfs(graph,visited,1);
for(int i = 1; i <= n; i++)
if(visited[i] == false)
{
cout << -1 << endl;
return 0;
}
cout << 1 << endl;
return 0;
}
106. 岛屿的周长
题目链接:106. 岛屿的周长
题目描述:
给定一个由 1(陆地)和 0(水)组成的矩阵,岛屿是被水包围,并且通过水平方向或垂直方向上相邻的陆地连接而成的。
你可以假设矩阵外均被水包围。在矩阵中恰好拥有一个岛屿,假设组成岛屿的陆地边长都为 1,请计算岛屿的周长。岛屿内部没有水域。
解题思路:
1)找边法
遍历每一个空格,遇到岛屿则计算其上下左右的空格情况。如果该陆地上下左右的空格是有水域,则说明是一条边;如果该陆地上下左右的空格出界了,则说明是一条边;
2)公式法
结果 result = 岛屿数量 * 4 - cover * 2;
有一对相邻两个陆地,边的总数就要减2
每次只统计节点上边相邻陆地和其左边相邻陆地,其余方向不统计防止重复。
代码:
1)dfs找边法
#include<iostream>
#include<vector>
using namespace std;
int cnt = 0;
int dx[4] = {-1,0,0,1};
int dy[4] = {0,-1,1,0};
void dfs(vector<vector<int>>& grid,vector<vector<bool>>& visited,int r, int c)
{
visited[r][c] = true;
for(int i = 0 ; i < 4; i++)
{
int x = r + dx[i], y = c + dy[i];
if(x < 0 || y < 0 || x >= grid.size() || y >= grid[0].size() || grid[x][y] == 0)
{
cnt++;
continue;
}
if(grid[x][y] == 1 && !visited[x][y])
dfs(grid,visited,x,y);
}
}
int main()
{
int n,m;
cin >> n >> m;
vector<vector<int>> grid(n,vector<int>(m));
vector<vector<bool>> visited(n,vector<bool>(m));
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
cin >> grid[i][j];
for(int i = 0 ; i < n; i++)
for(int j = 0; j < m; j++)
if(grid[i][j] == 1 && !visited[i][j])
{
dfs(grid,visited,i,j);
break;
}
cout << cnt << endl;
return 0;
}
2)直接找边法
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
int direction[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {
for (int k = 0; k < 4; k++) { // 上下左右四个方向
int x = i + direction[k][0];
int y = j + direction[k][1]; // 计算周边坐标x,y
if (x < 0 // x在边界上
|| x >= grid.size() // x在边界上
|| y < 0 // y在边界上
|| y >= grid[0].size() // y在边界上
|| grid[x][y] == 0) { // x,y位置是水域
result++;
}
}
}
}
}
cout << result << endl;
}
3)公式法
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
int sum = 0; // 陆地数量
int cover = 0; // 相邻数量
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {
sum++; // 统计总的陆地数量
// 统计上边相邻陆地
if(i - 1 >= 0 && grid[i - 1][j] == 1) cover++;
// 统计左边相邻陆地
if(j - 1 >= 0 && grid[i][j - 1] == 1) cover++;
// 为什么没统计下边和右边? 因为避免重复计算
}
}
}
cout << sum * 4 - cover * 2 << endl;
}