题目描述
给一 n×n 的字母方阵,内可能蕴含多个 yizhong
单词。单词在方阵中是沿着同一方向连续摆放的。摆放可沿着 8个方向的任一方向,同一单词摆放时不再改变方向,单词与单词之间可以交叉,因此有可能共用字母。输出时,将不是单词的字母用 *
代替,以突出显示单词。
输入格式
第一行输入一个数 n。(7≤n≤100)。
第二行开始输入 n×n 的字母矩阵。
输出格式
突出显示单词的 n×n 矩阵。
输入输出样例
输入 #1
7 aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
输出 #1
******* ******* ******* ******* ******* ******* *******
输入 #2
8 qyizhong gydthkjy nwidghji orbzsfgz hhgrhwth zzzzzozo iwdfrgng yyyygggg
输出 #2
*yizhong gy****** n*i***** o**z**** h***h*** z****o** i*****n* y******g
解题思路
首先看到这个题目,不就是一个普通的搜索题嘛,这有何难,但是真正做起来发现一个大问题,因为它这个单词是可以从八个方向拓展的,那么问题来了,我们从y开始该怎么去找这些满足条件的单词呢?这里很简单,用方向数组遍历即可,但是接踵而来的就是,我们知道了某一段是符合条件的,那怎么输出出来呢?题目要求输出的是变化后的所有方阵信息,其实我们可以利用一个点,就是方向数组他每一个都是指着一个方向,我们可以用一个数组去把满足条件的部分都标记位1,其他部分标记为0,这样我们就可以很轻松的输出里面的信息。就先这样(具体实现在代码中有注释)
void check(int x, int y) {
for (int k = 0; k < 8; k++) {
bool flag = 1;//每个方向都用一个flag来记录看是否有全部满足"yizhong"的
for (int i = 0; match[i]; i++) {
//每个方向上的偏移量
int dx = x + i * dir[k][0], dy = y + i * dir[k][1];
//相等就continue看下一个是否相等
if (words[dx][dy] == match[i]) continue;
flag = false;//不相等就让flag等于false然后退出内层循环
break;
}
if (flag) {
//把满足条件的方向进行标记
for (int i = 0; match[i]; i++) {
int dx = x + i * dir[k][0], dy = y + i * dir[k][1];
board[dx][dy] = true;
}
}
}
return ;
}
我们在搜索的时候,对于每一个方向我们都用一个flag来判断这个方向是否满足条件,如果满足条件呢,咱们就对着这一个方向给他全部标记上1,这样就可以啦。
接下来就是有关dfs问题了,话不多说先看代码,我们对着代码来看问题会简单许多
void dfs(int x, int y) {
if (words[x][y] == 0) return ;
vis[x][y] = true;
//等于y就开始搜索
if (words[x][y] == 'y') {
check(x, y);
}
//对八个方向进行搜索
for (int i = 0; i < 8; i++) {
int dx = x + dir[i][0], dy = y + dir[i][1];
//越界情况或者来过就不进行搜索
if (words[dx][dy] == 0 || vis[dx][dy]) continue;
dfs(dx, dy);//继续搜索
}
return ;
}
首先我在做这个题的时候遇到第一个问题就是在check函数之后直接返回,这样就导致每次检查完之后就返回了,压根没往后面进行搜索,这样就错了┭┮﹏┭┮,当时还找了好一会~!还有就是这个是似乎后面的搜索,没必要往八个方向去扩展,我们只需要向右和向下扩展就可以了,因为我们每一次搜索都是不需要返回搜索第二次的,这样我们只需要一直朝着终点搜索即可。像这样:
void dfs(int x, int y) {
if (words[x][y] == 0 || vis[x][y]) return ;
vis[x][y] = true;
//等于y就开始搜索
if (words[x][y] == 'y') {
check(x, y);
}
dfs(x + 1, y);
dfs(x, y + 1);
return ;
}
我们只需要在进入递归函数第一时间判断这个地方是不是来过和是否合法,如果没来过且合法我们就继续递归,否则就直接返回即可。
这个题的难点在于怎么去准确地表示每一段满足条件的位置然后去输出,这就需要我们对方向数组十分了解,并且知道灵活利用才行~
题解代码
其实主要代码都看的差不多啦,剩下都是些准备工作~
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
//方向数组
const int dir[8][2] = {
{0, 1}, {0, -1}, {1, 0}, {-1, 0},
{1, -1}, {-1, 1}, {-1, -1}, {1, 1}
};
const int MAXN = 150;//方阵的最大大小
const char match[8] = "yizhong";//目标字符串
char words[MAXN][MAXN] = {0};//输入
bool vis[MAXN][MAXN] = {0};//记录当前位置是否被用过
bool board[MAXN][MAXN] = {0};//记录满足条件的位置
int n;
//检查八个方向是否有满足条件的
void check(int x, int y) {
for (int k = 0; k < 8; k++) {
bool flag = 1;//每个方向都用一个flag来记录看是否有全部满足"yizhong"的
for (int i = 0; match[i]; i++) {
//每个方向上的偏移量
int dx = x + i * dir[k][0], dy = y + i * dir[k][1];
//相等就continue看下一个是否相等
if (words[dx][dy] == match[i]) continue;
flag = false;//不相等就让flag等于false然后退出内层循环
break;
}
if (flag) {
//把满足条件的方向进行标记
for (int i = 0; match[i]; i++) {
int dx = x + i * dir[k][0], dy = y + i * dir[k][1];
board[dx][dy] = true;
}
}
}
return ;
}
void dfs(int x, int y) {
if (words[x][y] == 0 || vis[x][y]) return ;
vis[x][y] = true;
//等于y就开始搜索
if (words[x][y] == 'y') {
check(x, y);
}
dfs(x + 1, y);
dfs(x, y + 1);
return ;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> (words[i] + 1);
dfs(1, 1);
//按题目要求输出结果
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (board[i][j]) cout << words[i][j];
else cout << "*";
}
cout << endl;
}
return 0;
}