字符串接龙
主要参考代码随想录 代码随想录 (programmercarl.com)
目标:得到从beginStr转变为endStr所需的最少步数
过程:每次变换一个字母,每次变换的结果要在strList中。
对于一个图来说,相当于我们有1个起点beginStr和一个终点endStr,以及strList中的N个字符串,然后我们需要找到路径来使得beginStr变成endStr,将beginStr连接到endStr的路径即为变化的过程,我们要最小化这个过程。
这里有两个问题:
- 图中的线是如何连在一起的。
- 如何确定当前路径最短。
对于线的连接,我们注意到每次只能变换一个字符,即我们需要判断是一个目标字符串与源字符串是否相差一个字符,若相差一个字符,则他们之间是连通的。
而判断路径最短,考虑使用广度优先算法,只要搜索到了结果,那得到的一定是最短路径,此外,注意这里计算的最短路径不是线的数目,而是节点的数目。
代码随想录里有两个点能方便计算:一是无向图需要标志位,来标记节点是否走过,使用set来检查字符串是否出现在字符串集合中较快。二是利用哈希表来映射字符串和路径长度。
具体代码如下,和代码随想录中结果一样。
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <queue>
using namespace std;
int main(){
int N;
cin>>N; // 输入字符串数量 N
unordered_set<string> strSet; // 创建一个无序集合存储所有字符串
string beginStr, endStr, str;
cin >> beginStr >>endStr; // 输入起始字符串和目标字符串
for(int i = 0; i < N; i++){
cin>>str; // 输入 N 个字符串
strSet.insert(str); // 将字符串插入集合
}
unordered_map<string , int> visitMap; // 创建一个无序映射记录字符串和对应的路径长度
queue<string> Queue; // 创建一个队列用于 BFS
Queue.push(beginStr); // 将起始字符串入队列
visitMap.insert(pair<string,int>(beginStr, 1)); // 起始字符串路径长度为 1
while(!Queue.empty()){ // 当队列不为空时进行 BFS
string word = Queue.front(); // 获取队列首部的字符串
Queue.pop();
int path = visitMap[word]; // 获取该字符串的路径长度
for(int i = 0;i < word.size();i++){ // 遍历字符串的每个字符
string newWord = word; // 创建一个新字符串,用于修改字符
for(int j = 0; j < 26; j++){ // 遍历 'a' 到 'z',ASCII码
newWord[i] = 'a' + j; // 修改字符串的一个字符
if(newWord == endStr){ // 如果新字符串是目标字符串
cout<<path + 1<<endl; // 输出路径长度并结束程序
return 0;
}
if(strSet.find(newWord)!= strSet.end() && visitMap.find(newWord) == visitMap.end()){
// 如果新字符串在集合中且未被访问过
visitMap.insert(pair<string,int>(newWord,path + 1)); // 记录新字符串和路径长度
Queue.push(newWord); // 将新字符串入队列
}
}
}
}
cout<< 0 <<endl; // 如果没有找到路径,输出 0
return 0;
}
对每个字符串,长度定位L,在BFS的过程中,每个字符串都可能被访问一次,每个源字符串的每个字符都有修改的可能,共有26*L次操作,由于每个字符串只被访问一次,因此总的时间复杂度为O(N*26*L)。
对空间复杂度,主要需要一个集合、一个哈希表及一个队列,空间复杂度都为O(N*L),总体空间复杂度为O(N*L)。
有向图的完全可达性
用广度优先搜索,对1的每个可达点进行遍历,并将可达点加入set,最后判断set的长度是否等于节点数即可。
#include <iostream>
#include <vector>
#include <unordered_set>
#include<queue>
using namespace std;
int main(){
int N,K;
cin>>N>>K; // 输入节点总数 N 和边的总数 K
vector<vector<int>> dirgraph(K,vector<int>(2,0)); // 创建一个二维向量,用于存储有向图的边
for(int i = 0 ; i < K; i++){
cin>>dirgraph[i][0]>>dirgraph[i][1]; // 输入每条边的起始节点和终止节点
}
unordered_set<int> visited; // 创建一个无序集合,用于存储已访问的节点
visited.insert(1); // 将起始节点 1 加入已访问集合
queue<int> Queue; // 创建一个队列,用于 BFS
Queue.push(1); // 将起始节点 1 加入队列
while(!Queue.empty()){ // 当队列不为空时进行 BFS
int x = Queue.front(); // 获取队列首部的节点
Queue.pop(); // 出队列
for(int i = 0; i < K;i++){ // 遍历所有的边
if(dirgraph[i][0] == x && visited.find(dirgraph[i][1]) == visited.end()) {
// 如果边的起始节点是 x 并且终止节点未被访问过
visited.insert(dirgraph[i][1]); // 将终止节点加入已访问集合
Queue.push(dirgraph[i][1]); // 将终止节点加入队列
}
}
}
if(visited.size()==N) // 如果已访问的节点数等于总节点数
cout<<1<<endl;
else{ // 否则
cout<<-1<<endl;
}
return 0;
}
算法的时间复杂度为O(N*K),空间复杂度为O(N+K)。
岛屿的周长
emmm,想了半天dfs和bfs,发现并不需要。。。
遍历每一个空格,遇到岛屿就计算其上下左右的领域情况,若上下左右为0或超出区域,则是一条边。遍历完所有的空格和每个空格的领域,即得到结果。
#include <iostream>
#include <vector>
using namespace std;
int result = 0; // 定义一个全局变量,用于存储岛屿周长
int main() {
int N,M;
cin>>N>>M; // 输入网格的行数 N 和列数 M
vector<vector<int>> graph(N,vector<int>(M)); // 创建一个 N 行 M 列的二维向量,用于存储网格
for(int i = 0; i < N; i++) // 遍历网格的每一行
for (int j = 0; j < M; j++) // 遍历网格的每一列
cin >> graph[i][j]; // 输入网格的值
for(int i = 0; i < N; i++){ // 遍历网格的每一行
for(int j = 0; j < M; j++){ // 遍历网格的每一列
if(graph[i][j] == 1){ // 如果当前单元格是土地
if((i + 1) == N || graph[i+1][j] == 0) // 如果下方是网格边缘或水
result++; // 结果计数器加 1
if((i - 1) == -1 || graph[i-1][j] == 0) // 如果上方是网格边缘或水
result++; // 结果计数器加 1
if(j + 1 == M || graph[i][j + 1] == 0) // 如果右方是网格边缘或水
result++; // 结果计数器加 1
if(j - 1 == -1 || graph[i][j - 1] == 0) // 如果左方是网格边缘或水
result++; // 结果计数器加 1
}
}
}
cout<<result<<endl; // 输出岛屿周长
return 0;
}
算法的时间复杂度为O(4*N*M),空间复杂度为O(1)。

被折叠的 条评论
为什么被折叠?



