1
/*
2
最短路径的一个举例:
3
4
问题描述:在字梯游戏中,每一个词语都是通过将字梯的前一个词改变一个字母形成的。
5
例如,我们可以通过一系列的单字母替换将zero转换成five:zero, hero, here, hire, fire, five。
6
7
这个是一个无权最短路径问题,每个词语是个顶点,如果两个顶点可以通过一个字母的替换相互转换的话,
8
这两个顶点就有一个双向的边。
9
10
首先,我们要从一个字典中生成这么一个图:使用STL中的map,其中键是单词(string),
11
值是一个通过此单词一次变换能够得到的单词的集合vector<string>。
12
问题的关键在于:如何从一个包含89000个单词的词组来构造map。
13
可以用蛮力法,但是时间太慢,所有单词之间都要比较。一个优化是:避免比较长度不同的单词。
14
这个可以通过将单词按照长度先分组来做到。
15
16
一个方法是:对每个相同长度的数组用蛮力法。
17
另一个方法是:附加一个map,其中的键是一个反映单词长度的int,值是所有的具有这个长度的单词集合。
18
思想还是同上。
19
第三个方法是:使用附加map。将单词按照长度分组,对每个组分别操作。例如对长度为4的小组。
20
对每个长度为4的单词删除第一个字母,保留剩下的三个字母的样本,生成一个map,其中的键是这个样本,
21
值是所有的有这个样本单词的vector。例如单词"wine",去掉'w'生成样本"ine",则样本"ine"对应的
22
vector是"dine", "fine", "wine", "nine"
.,样本"oot"对应:boot, foot, hoot, loot
..最后的这个map的值(vector)就是
23
一组单词,其中每个单词都能够通过一个字符的替换生成其他的单词。
24
*/
25
26
#include <iostream>
27
#include <sstream>
28
#include <algorithm>
29
#include <string>
30
#include <map>
31
#include <set>
32
#include <list>
33
#include <stack>
34
#include <queue>
35
#include <cctype>
36
#include <vector>
37
#include <bitset>
38
#include <cmath>
39
//#include <hash_map>
40
41
#define FORR(i, a, b) for(int i = a; i < b; ++i)
42
#define BE(x) x.begin(),x.end()
43
#define MP(a, b) make_pair(a, b)
44
45
using namespace std;
46
//using namespace stdex;
47
48
typedef vector<string> VS;
49
typedef vector<int> VI;
50
51
//words是所有的单词
52
map<string, VS> computerAdjacentWords(const VS& words)
53
{
54
map<string, VS> adjWords;
55
map<int, VS> wordsByLength;
56
57
//按长度分组
58
for (int i = 0; i < words.size(); i++)
59
{
60
wordsByLength[words[i].length()].push_back(words[i]);
61
}
62
map<int, VS>::const_iterator itr;
63
//对每个分组操作
64
for (itr = wordsByLength.begin(); itr != wordsByLength.end(); ++itr)
65
{
66
const VS& groupsWords = itr->second;
67
int groupNum = itr->first;
68
69
//操作每个组中的每个位置
70
for (int i = 0; i < groupNum; i++)
71
{
72
//形如:"ine" -> "fine", "wine", "nine"
.
73
map<string, VS> repToWord;
74
75
for (int j = 0; j < groupsWords.size(); j++)
76
{
77
string rep = groupsWords[j];
78
rep.erase(i, 1);
79
//map的无重复性就是好,而且如果没有对应的可以直接新建一个
80
//wine加到ine中,fine也加到ine中
81
//wine也加到wne中
82
repToWord[rep].push_back(groupsWords[j]);
83
}
84
85
// 然后查找map中的值不止一个单词的(就是有像wine,fine这种)
86
map<string, VS>::const_iterator itr2;
87
for (itr2 = repToWord.begin(); itr2 != repToWord.end(); ++itr2)
88
{
89
const VS& clique = itr2->second;
90
if (clique.size() >= 2)
91
{
92
for (int p = 0; p < clique.size(); p++)
93
{
94
for (int q = p + 1; q < clique.size(); q++)
95
{
96
adjWords[clique[p]].push_back(clique[q]);
97
adjWords[clique[q]].push_back(clique[p]);
98
}
99
}
100
}
101
}
102
}
103
}
104
return adjWords;
105
}
106
107
/*
108
下面就要使用单元无权最短路径算法了:
109
上面生成的map就相当是一个邻接链表的图表示:
110
一个节点和所有与它相连的节点集合。
111
由于,单源无权最短路径算法只能给出每个节点在路径中的前驱节点,所以,
112
这里返回的map<string,string>值就是最短路径中每个节点的相应前驱节点,而map中所有的键就是
113
最短路径中的所有节点。例如:将zero转换成five:zero, hero, here, hire, fire, five,则,
114
findChain返回的map<string,string>previousWord就是previousWord[five] = fire, previousWord[here] = hero。
115
注意:这里是找到从zero出发到所有节点的路径(中节点的前驱集合,所以没有second参数)
116
*/
117
map<string, string> findChain(const map<string, VS> & adjacentWords, const string& first)
118
{
119
map<string, string> previousWord;
120
queue<string> q;
121
122
q.push(first);
123
while (!q.empty())
124
{
125
string current = q.front();
126
q.pop();
127
128
map<string, VS>::const_iterator itr;
129
itr = adjacentWords.find(current);
130
131
const VS& adj = itr->second;
132
for (int i =0; i < adj.size(); i++)
133
{
134
if (previousWord[adj[i]] == "")
135
{
136
previousWord[adj[i]] = current;
137
q.push(adj[i]);
138
}
139
}
140
}
141
previousWord[first] = "";
142
143
return previousWord;
144
}
145
146
VS getChainFromPrevMap(const map<string, string>& previous, const string& second)
147
{
148
VS result;
149
//类型转换,因为操作符[]不能用在不可变的map中
150
map<string, string>& prev = const_cast<map<string, string> &> (previous);
151
152
for (string current = second; current != ""; current = prev[current])
153
result.push_back(current);
154
155
reverse(result.begin(), result.end());
156
157
return result;
158
}
159
160
int main()
161
{
162
string a[] = {"five", "hero", "boyfriend", "james", "zero", "good", "hire", "thank", "fire",
163
"here", "pen", "ccbb", "greatman", "come", "great", "greet", "gold", "glad"};
164
VS input(a, a+18);
165
string first("zero"), second("five");
166
167
//先构造一个图
168
map<string, VS> adjWords = computerAdjacentWords(input);
169
170
//再构造某个节点到所有节点最短路径中先驱集合
171
map<string, string> previousWords = findChain(adjWords, first);
172
173
//在找出到某个终点的最短路径
174
VS result = getChainFromPrevMap(previousWords, second);
175
176
FORR(i, 0, result.size())
177
{
178
cout << result[i] << " ";
179
}
180
cout << endl;
181
182
return 0;
183
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22




23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72


73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183
