给定两个单词(开始词与结束词)及字典列表,找出可以从开始词到结束词之间最短的转换序列,转换规则:
如给定如下的开始词,结束词及字典列表:
beginWord = "hit"
endWord = "cog"
dictList = ["hot","dot","dog","lot","log","cog"]
我们可以找到两条最短的转换序列
hit->hot->dot->dog->cog
hit->hot->lot->log->cog
输入
输入包含两行,第一行输入开始词及结束词,第二行输入字典列表。所有单词仅包含 a~z 的小写字母,单词最大长度为 5,字典最多包含 5000 个单词,开始词与结束词均不为空且不相等,同时字典中无重复单词。
输出
如存在最短的序列,请按行输出,多个序列按字典序升序排列;如未找到最短的序列,输出 "No Solution"
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 以文本方式显示
|
以文本方式显示
|
1秒 | 1024KB | 0 |
题解:
这题挺难的,大部分的方法基本上是构造邻接矩阵或邻接表,而且网上基本上给的都是C++或java等语言实现的,因为有比较好的函数库支持一些写法的实现,用纯C来实现就比较麻烦。讨论区里杨珏成大佬给了一个比较有新意的方法(我表示真的真的服了),使用五维数组加BFS,然后使用回溯表输出结果。接下来我将结合我个人实际操作进行讲解。
先构建一个字典Instr_char[5001][6]={'\0'},这个字典用来记录输入的单词(第一行的首单词可以用字典第一行InStr_char[0]记录,尾单词用另一个char型数组EdStr记录)。然后是构造五维数组StrMap[27][27][27][27][27],这个是判断单词是否存在的,我是建的结构体数组,因为我当时想为了后续的回溯表,所以还用一个值index记录这个单词在字典中的位置,本身值num赋为5001,因为单词数最多为5000。
搜索部分:
对于每个队首结点
进入一个 j:0->25的for循环,
每一次的循环中分别将队首字符串的第0-4位替换为j,查找五维地图上新字符串是否存在
即分别查找StrMap[i][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num是否存在
若存则进入第二步判断:
如果当前步数小于StrMap.num上储存的值,则入队当前字符,并将num该位赋为当前步数
否则不进入操作
为什么要这样?
因为到达同一个中间词时,
也有可能有多条同样步长的变换序列,
这些序列均需入队,这意味着中间节点可能多次入队,
不能直接用vis数组标1,而要考虑当前步长是否仍是最短路
此外要注意字典序!!字典序!!字典序!!
先说上述方法的问题:
对于字符串aaaaa,可能先入队aabaa,后入队aaaac,然而明显后者字典序更靠前
对于以字典序入队部分:(这个字典序入队操作的方法是参考张浩大佬的思路)
做十个循环(Ctr C...)
比如hit
先循环第一个字母h
a-h,
再入第二个字母
a-i,
接着第三个字母
a-t;
然后还是第三个字母
h-z;
再是第二个字母
i-z;
最后第一个字母
h-z;
这样就能保证字典序。(可以手动模拟一下,杨珏成大佬是先入队后用排序的,个人感觉比较麻烦)
回溯部分:
就是子节点记录父节点,这个记录可以放在队列里,因为是纯C模拟队列,所以开的数组不会清空,不然像使用C++的STL的话还要重新开数组记录,输出就是递归输出就好。
下面给出杨珏成大佬和张浩大佬的源攻略:
杨珏成大佬的:
这个代码出现玄学错误了,之前一直没事,提交后也顺利AC,结果暑假有次有同学找我要代码发现这个代码无法输出结果,我把AC的提交历史记录调出来重新运行也是无法输出。。。尴尬。。。有大佬知道这是什么情况吗?若有大佬知道发现bug点,麻烦告知一下,感激不尽!
#include//五维数组加BFS的方法
#include
#include
int LenStr;//串长
short int InStr_int[5006][5];//承担将char型变成int型的记录
char EdStr[6] = { '\0' };//记录终点
int head = 0, end = 0;//队列移动的指针
struct node1
{
short int num;//第一次标记5001,表示这个单词是存在的,之所以用5001标记,是因为最最最大步数不超过5000,为了接下来在防循环方面的操作故标记5001
short int index;//记录当前单词在字典中的位置
};
struct node1 StrMap[27][27][27][27][27];//构建五维数组,主要用来判断这个单词是否存在
char InStr_char[5001][6] = { '\0' };//构建字典
struct node3
{
short int index;//记录本元素的串在字典中的位置
short int step;//步数
int FatherIndex;//记录父亲队列元素在队列中的位置,方便后来回溯输出
int queue_index;//记录本元素在队列中的位置
};
struct node3 Queue[100005];
int Q_empty()//队列是否为空
{
if (head == end)
return 1;
else
return 0;
}
void Q_in(struct node3 InI)//入队
{
Queue[end] = InI;
Queue[end].queue_index = end;
end++;
}
void Print(int Fatherindex)//回溯输出
{
if (Fatherindex == 0)
{
printf("%s", InStr_char[Queue[Fatherindex].index]);
return;
}
Print(Queue[Fatherindex].FatherIndex);
printf("->%s", InStr_char[Queue[Fatherindex].index]);
}
int main()
{
int StrNum = 1, lens;
for (int i = 0; i < 5001;)//不知道为啥用memset给InStr_int赋值26会出现错误,故只能用循环了
{
InStr_int[i][0] = 26; InStr_int[i][1] = 26; InStr_int[i][2] = 26; InStr_int[i][3] = 26; InStr_int[i][4] = 26;
InStr_int[i + 1][0] = 26; InStr_int[i + 1][1] = 26; InStr_int[i + 1][2] = 26; InStr_int[i + 1][3] = 26; InStr_int[i + 1][4] = 26;
InStr_int[i + 2][0] = 26; InStr_int[i + 2][1] = 26; InStr_int[i + 2][2] = 26; InStr_int[i + 2][3] = 26; InStr_int[i + 2][4] = 26;
InStr_int[i + 3][0] = 26; InStr_int[i + 3][1] = 26; InStr_int[i + 3][2] = 26; InStr_int[i + 3][3] = 26; InStr_int[i + 3][4] = 26;
InStr_int[i + 4][0] = 26; InStr_int[i + 4][1] = 26; InStr_int[i + 4][2] = 26; InStr_int[i + 4][3] = 26; InStr_int[i + 4][4] = 26;
i += 5;
}
memset(StrMap, 0, sizeof(StrMap));
scanf("%s %s", InStr_char[0], EdStr);
LenStr = strlen(InStr_char[0]);
for (int i = 0; i < LenStr; i++)
InStr_int[0][i] = InStr_char[0][i] - 'a';
StrMap[InStr_int[0][0]][InStr_int[0][1]][InStr_int[0][2]][InStr_int[0][3]][InStr_int[0][4]].num = 5001;
StrMap[InStr_int[0][0]][InStr_int[0][1]][InStr_int[0][2]][InStr_int[0][3]][InStr_int[0][4]].index = 0;
while (~scanf("%s", InStr_char[StrNum]))//读入字典
{
lens = strlen(InStr_char[StrNum]);
if (strcmp(InStr_char[0], InStr_char[StrNum]) == 0)
{
memset(InStr_char[StrNum], '\0', sizeof(InStr_char[StrNum]));
continue;
}
for (int i = 0; InStr_char[StrNum][i] != '\0'; i++)
InStr_int[StrNum][i] = InStr_char[StrNum][i] - 'a';
if (lens == LenStr)
StrMap[InStr_int[StrNum][0]][InStr_int[StrNum][1]][InStr_int[StrNum][2]][InStr_int[StrNum][3]][InStr_int[StrNum][4]].num = 5001;
StrMap[InStr_int[StrNum][0]][InStr_int[StrNum][1]][InStr_int[StrNum][2]][InStr_int[StrNum][3]][InStr_int[StrNum][4]].index = StrNum;
StrNum++;
}
struct node3 start, now, next;//start初始入队元素,now当前取队元素,next准备接下来入队元素
start.index = 0; start.step = 1; start.FatherIndex = 0;
Q_in(start);
int flag = 5002;//用来标记最短路径数
while (!Q_empty())
{
now = Queue[head];
head++;
if (now.step > flag)//如果步数超过最短路数,要退出
break;
if (strcmp(InStr_char[now.index], EdStr) == 0)//到达终点
{
Print(now.FatherIndex);
printf("->%s\n", InStr_char[now.index]);
flag = now.step;
continue;
}
//接下来是直接以字典序入队,也可以先入队后字典序排序 ,不过会有重复入队情况
if (LenStr >= 1)
{
for (int i = 0; i < InStr_int[now.index][0]; i++)
{
if (StrMap[i][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num >= now.step)
{
StrMap[i][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num = now.step;
next.step = now.step + 1;
next.index = StrMap[i][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 2)
{
for (int i = 0; i < InStr_int[now.index][1]; i++)
{
if (StrMap[InStr_int[now.index][0]][i][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num >= now.step)
{
StrMap[InStr_int[now.index][0]][i][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num = now.step;
next.step = now.step + 1;
next.index = StrMap[InStr_int[now.index][0]][i][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 3)
{
for (int i = 0; i < InStr_int[now.index][2]; i++)
{
if (StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][i][InStr_int[now.index][3]][InStr_int[now.index][4]].num >= now.step)
{
StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][i][InStr_int[now.index][3]][InStr_int[now.index][4]].num = now.step;
next.step = now.step + 1;
next.index = StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][i][InStr_int[now.index][3]][InStr_int[now.index][4]].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 4)
{
for (int i = 0; i < InStr_int[now.index][3]; i++)
{
if (StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][i][InStr_int[now.index][4]].num >= now.step)
{
StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][i][InStr_int[now.index][4]].num = now.step;
next.step = now.step + 1;
next.index = StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][i][InStr_int[now.index][4]].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 5)
{
for (int i = 0; i < InStr_int[now.index][4]; i++)
{
if (StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][i].num >= now.step)
{
StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][i].num = now.step;
next.step = now.step + 1;
next.index = StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][i].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 5)
{
for (int i = InStr_int[now.index][4] + 1; i < 26; i++)
{
if (StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][i].num >= now.step)
{
StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][i].num = now.step;
next.step = now.step + 1;
next.index = StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][i].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 4)
{
for (int i = InStr_int[now.index][3] + 1; i < 26; i++)
{
if (StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][i][InStr_int[now.index][4]].num >= now.step)
{
StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][i][InStr_int[now.index][4]].num = now.step;
next.step = now.step + 1;
next.index = StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][InStr_int[now.index][2]][i][InStr_int[now.index][4]].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 3)
{
for (int i = InStr_int[now.index][2] + 1; i < 26; i++)
{
if (StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][i][InStr_int[now.index][3]][InStr_int[now.index][4]].num >= now.step)
{
StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][i][InStr_int[now.index][3]][InStr_int[now.index][4]].num = now.step;
next.step = now.step + 1;
next.index = StrMap[InStr_int[now.index][0]][InStr_int[now.index][1]][i][InStr_int[now.index][3]][InStr_int[now.index][4]].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 2)
{
for (int i = InStr_int[now.index][1] + 1; i < 26; i++)
{
if (StrMap[InStr_int[now.index][0]][i][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num >= now.step)
{
StrMap[InStr_int[now.index][0]][i][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num = now.step;
next.step = now.step + 1;
next.index = StrMap[InStr_int[now.index][0]][i][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
if (LenStr >= 1)
{
for (int i = InStr_int[now.index][0] + 1; i <26; i++)
{
if (StrMap[i][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num >= now.step)
{
StrMap[i][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].num = now.step;
next.step = now.step + 1;
next.index = StrMap[i][InStr_int[now.index][1]][InStr_int[now.index][2]][InStr_int[now.index][3]][InStr_int[now.index][4]].index;
next.FatherIndex = now.queue_index;
Q_in(next);
}
}
}
}
if (flag == 5002)//未找到最短的序列
printf("No Solution\n");
return 0;
}