在遥远的算法大陆,有一座神秘的城市——“迷宫之城”。在这座城市的中心,矗立着一座名为“命运之塔”的古老建筑。传说中,这里藏着一本神秘的典籍——《剑指天书》,书中记载着无数关于矩阵、路径和逻辑推理的奥秘。
在这片土地上,有一种被称为 “矩阵迷宫” 的古老魔法阵。它由一个个字符格子组成,每一步只能向上下左右移动一格。而最神奇的是,如果一条路径经过了某个格子,就不能再次进入这个格子。例如:
A B T G
C F C S
J D E H
在这个迷宫中,存在一条路径可以走出字符串 "BFCE"
:
B
→ F
→ C
→ E
(下标路径)
但同样的迷宫中却无法走出 "ABFB"
,因为第一个 B
已经被占用了,不能再回头使用。
人们相信,只有找到包含目标字符串所有字符的路径,才能激活迷宫的核心力量,获得通往更深层知识的钥匙。
在一个晨光微露的清晨,年轻的解阵师 小海 带着他的剑与笔记本,走进了“命运之塔”。他立志要解开矩阵迷宫中的路径之谜。
bool hasPath(const char* matrix, int rows, int cols, const char* str)
{
// 检查输入是否合法:空矩阵或空字符串都视为非法输入
if(matrix == nullptr || rows < 1 || cols < 1 || str == nullptr)
return false;
bool *visited = new bool[rows * cols]; // 创建一个访问标记数组,初始化为false
memset(visited, 0, rows * cols); // 将访问标记数组的所有元素设置为未访问状态
int pathLength = 0; // 记录当前匹配到的目标字符串的长度
for(int row = 0; row < rows; ++row) // 遍历整个矩阵
{
for(int col = 0; col < cols; ++col)
{
if(hasPathCore(matrix, rows, cols, row, col, str,
pathLength, visited)) // 调用核心探索术尝试从当前位置开始匹配
{
delete[] visited; // 释放访问标记数组
return true; // 找到路径,返回true
}
}
}
delete[] visited; // 释放访问标记数组
return false; // 未找到路径,返回false
}
这段咒语召唤出一个核心探索术 hasPathCore
,用于在迷宫中递归探索路径。
// 回溯算法原理注释:
// 回溯是一种通过构建所有可能的解决方案,并逐步排除不符合条件的方案来解决问题的策略。
// 在此情境中,我们试图找到一条从矩阵任意位置出发,能够顺序匹配给定字符串的路径。
// 如果当前步骤不能继续前进,则撤销前一步的选择(即回退),并尝试其他可能的方向。
bool hasPathCore(const char* matrix, int rows, int cols, int row,
int col, const char* str, int& pathLength, bool* visited)
{
// 如果已经匹配到了目标字符串的末尾,则返回true
if(str[pathLength] == '\0')
return true;
bool hasPath = false;
// 检查当前位置是否有效且未访问,并且匹配目标字符串的当前字符
if(row >= 0 && row < rows && col >= 0 && col < cols
&& matrix[row * cols + col] == str[pathLength]
&& !visited[row * cols + col])
{
++pathLength; // 匹配成功,增加匹配长度
visited[row * cols + col] = true; // 标记当前位置已访问
// 尝试向四个方向继续搜索
hasPath = hasPathCore(matrix, rows, cols, row, col - 1, str, pathLength, visited) // 向左
|| hasPathCore(matrix, rows, cols, row - 1, col, str, pathLength, visited) // 向上
|| hasPathCore(matrix, rows, cols, row, col + 1, str, pathLength, visited) // 向右
|| hasPathCore(matrix, rows, cols, row + 1, col, str, pathLength, visited); // 向下
// 如果未能找到路径,则回退并取消当前位置的访问标记
if(!hasPath)
{
--pathLength; // 回退匹配长度
visited[row * cols + col] = false; // 取消访问标记
}
}
return hasPath;
}
在这段代码中,回溯算法的工作原理是这样的:
选择路径:从矩阵的一个点开始尝试,看看能否匹配字符串的第一个字符。
递归探索:一旦匹配成功,递归地尝试从该点出发的四个方向(上下左右)继续匹配下一个字符。
回退机制:如果某条路径走不通(比如到达了死胡同或者无法匹配剩余的字符),则撤销前一步的选择,回到上一个节点,尝试其他可能的路径。
终止条件:当成功匹配完字符串的所有字符时,表示找到了有效的路径;否则,在所有可能性都被尝试后仍无法找到路径,则表示不存在符合条件的路径。
小海:“导师,我在思考如何处理那些重复字符的问题,比如这个矩阵中有很多相同的字符。”
导师:“很好,这是个关键点。你需要确保每次遍历时不会重复访问同一个位置。你现在的做法是正确的,记得每次成功匹配后都要标记该位置为已访问,一旦发现此路径不通则需要回退。”
输入矩阵:
A B T G
C F C S
J D E H
目标字符串:"BFCE"
输出:✅ 成功找到路径!
阵法顺利解开!从
B
开始,依次找到F
,C
, 和E
。
小海的朋友:“小海,我注意到有些情况下即使矩阵中有足够的字符,但由于它们不能形成连续的路径,也无法匹配。”
小海:“确实如此,这就是为什么我们需要仔细检查每个字符周围的邻居,并确保没有重复访问同一个位置。”
输入矩阵:
A B C E
S F C S
A D E E
目标字符串:"SEE"
输出:✅ 成功找到路径!
尽管有两个
E
,只要不重复走同一个格子即可!
输入矩阵:
A B T G
C F C S
J D E H
目标字符串:"ABFB"
输出:❌ 路径冲突,无法完成!
第一个
B
已经被占用,无法回头!
输入矩阵:
A B C E H J I G
S F C S L O P Q
A D E E M N O E
A D I D E J F M
V C E I F G G S
目标字符串:"SLHECCEIDEJFGGFIE"
输出:✅ 成功找到路径!
多层回溯,最终寻得真相!
小海:“我发现,处理这些复杂情况的关键在于理解回溯的概念,即当我们遇到死胡同时,必须能够回到上一步,重新选择不同的路径。”
导师:“非常好,正是这种思维让你能够在面对任何挑战时都不慌不忙,找到解决之道。”
夕阳西下,小海站在“命运之塔”的顶端,手捧《剑指天书》,成为新一代的“迷宫宗师”。
他向后来者讲述自己的经历:
“每一个矩阵迷宫,都是一个世界;每一段代码,都是一把钥匙。只有理解它的规则,掌握它的边界条件,你才能真正驾驭它。”
孩子们围坐在他身边,认真聆听,眼中闪烁着对未来的憧憬。
从此,“矩阵迷宫的路径密钥”不仅成为了解阵术的象征,也成为了每一位踏上算法之路的旅者必经的一课。
“当你面对迷宫,不要慌张;只需冷静分析,就能从路径中找出通往真理的道路。” —— 解阵师小海,留于《剑指天书·卷十四》
// 更多测试案例...
void Test(const char* testName, const char* matrix, int rows, int cols, const char* str, bool expected)
{
printf("%s begins: ", testName);
if(hasPath(matrix, rows, cols, str) == expected)
printf("Passed.\n");
else
printf("FAILED.\n");
}
int main()
{
// 测试案例...
Test("Test1", "ABTGCFCSJDEH", 3, 4, "BFCE", true);
Test("Test2", "ABCESFCSADEE", 3, 4, "SEE", true);
Test("Test3", "ABTGCFCSJDEH", 3, 4, "ABFB", false);
Test("Test4", "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS", 5, 8, "SLHECCEIDEJFGGFIE", true);
Test("Test5", "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS", 5, 8, "SGGFIECVAASABCEHJIGQEM", true);
Test("Test6", "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS", 5, 8, "SGGFIECVAASABCEEJIGOEM", false);
Test("Test7", "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS", 5, 8, "SGGFIECVAASABCEHJIGQEMS", false);
Test("Test8", "AAAAAAAAAAAA", 3, 4, "AAAAAAAAAAAA", true);
Test("Test9", "AAAAAAAAAAAA", 3, 4, "AAAAAAAAAAAAA", false);
Test("Test10", "A", 1, 1, "A", true);
Test("Test11", "A", 1, 1, "B", false);
Test("Test12", nullptr, 0, 0, nullptr, false);
return 0;
}