43
43
44
44
** 方法一:BFS**
45
45
46
+ 本题实际上是一类经典的问题:求解最小操作次数。从一个初始状态 $s_1$,经过最少 $k$ 次状态转换,变成目标状态 $s_2$。字符串长度不超过 $20$,我们考虑使用 BFS 搜索来求解。
47
+
48
+ 首先将初始状态 $s_1$ 入队,用哈希表 ` vis ` 记录所有访问过的状态。
49
+
50
+ 接下来每一轮,都是将队列中的所有状态转换到下一个状态,当遇到目标状态 $s_2$ 时,当前状态转换的轮数就是答案。
51
+
52
+ 我们发现,题目的重点在于如何进行状态转换。对于本题,转换操作就是交换一个字符串中两个位置的字符。如果当前字符串 $s[ i] $ 与 $s_2[ i] $ 不相等,那么我们应该在 $s$ 中找到一个位置 $j$,满足 $s[ j] = s_2[ i] $ 并且 $s[ j] \neq s_2[ j] $,然后交换 $s[ i] $ 和 $s[ j] $。这样可以使得状态最接近于目标状态。这里的状态转换可以参考以下代码中的 ` next() ` 函数。
53
+
54
+ 复杂度分析:BFS 剪枝不讨论时空复杂度。
55
+
56
+ ** 方法二:A\* 算法(进阶)**
57
+
58
+ A\* 算法主要思想如下:
59
+
60
+ 1 . 将方法一中的 BFS 队列转换为优先队列(小根堆);
61
+ 1 . 队列中的每个元素为 ` (dist[state] + f(state), state) ` ,` dist[state] ` 表示从起点到当前 state 的距离,` f(state) ` 表示从当前 state 到终点的估计距离,这两个距离之和作为堆排序的依据;
62
+ 1 . 当终点第一次出队时,说明找到了从起点到终点的最短路径,直接返回对应的 step;
63
+ 1 . ` f(state) ` 是估价函数,并且估价函数要满足 ` f(state) <= g(state) ` ,其中 ` g(state) ` 表示 state 到终点的真实距离;
64
+ 1 . A\* 算法只能保证终点第一次出队时,即找到了一条从起点到终点的最小路径,不能保证其他点出队时也是从起点到当前点的最短路径。
65
+
66
+ 复杂度分析:启发式搜索不讨论时空复杂度。
67
+
46
68
<!-- tabs:start -->
47
69
48
70
### ** Python3**
@@ -54,18 +76,18 @@ class Solution:
54
76
def kSimilarity (self , s1 : str , s2 : str ) -> int :
55
77
def next (s ):
56
78
i = 0
57
- res = []
58
79
while s[i] == s2[i]:
59
80
i += 1
81
+ res = []
60
82
for j in range (i + 1 , n):
61
83
if s[j] == s2[i] and s[j] != s2[j]:
62
- res.append(s[:i] + s[j ] + s[i + 1 : j] + s[i] + s[j + 1 :])
84
+ res.append(s2[: i + 1 ] + s[i + 1 : j] + s[i] + s[j + 1 :])
63
85
return res
64
86
65
87
q = deque([s1])
66
88
vis = {s1}
67
89
ans, n = 0 , len (s1)
68
- while q :
90
+ while 1 :
69
91
for _ in range (len (q)):
70
92
s = q.popleft()
71
93
if s == s2:
@@ -75,7 +97,36 @@ class Solution:
75
97
vis.add(nxt)
76
98
q.append(nxt)
77
99
ans += 1
78
- return - 1
100
+ ```
101
+
102
+ ``` python
103
+ class Solution :
104
+ def kSimilarity (self , s1 : str , s2 : str ) -> int :
105
+ def f (s ):
106
+ cnt = sum (c != s2[i] for i, c in enumerate (s))
107
+ return (cnt + 1 ) >> 1
108
+
109
+ def next (s ):
110
+ i = 0
111
+ while s[i] == s2[i]:
112
+ i += 1
113
+ res = []
114
+ for j in range (i + 1 , n):
115
+ if s[j] == s2[i] and s[j] != s2[j]:
116
+ res.append(s2[: i + 1 ] + s[i + 1 : j] + s[i] + s[j + 1 :])
117
+ return res
118
+
119
+ q = [(f(s1), s1)]
120
+ dist = {s1: 0 }
121
+ n = len (s1)
122
+ while 1 :
123
+ _, s = heappop(q)
124
+ if s == s2:
125
+ return dist[s]
126
+ for nxt in next (s):
127
+ if nxt not in dist or dist[nxt] > dist[s] + 1 :
128
+ dist[nxt] = dist[s] + 1
129
+ heappush(q, (dist[nxt] + f(nxt), nxt))
79
130
```
80
131
81
132
### ** Java**
@@ -90,13 +141,13 @@ class Solution {
90
141
q. offer(s1);
91
142
vis. add(s1);
92
143
int ans = 0 ;
93
- while (! q . isEmpty() ) {
144
+ while (true ) {
94
145
for (int i = q. size(); i > 0 ; -- i) {
95
- s1 = q. poll ();
96
- if (s1 . equals(s2)) {
146
+ String s = q. pollFirst ();
147
+ if (s . equals(s2)) {
97
148
return ans;
98
149
}
99
- for (String nxt : next(s1 , s2)) {
150
+ for (String nxt : next(s , s2)) {
100
151
if (! vis. contains(nxt)) {
101
152
vis. add(nxt);
102
153
q. offer(nxt);
@@ -105,15 +156,70 @@ class Solution {
105
156
}
106
157
++ ans;
107
158
}
108
- return - 1 ;
109
159
}
110
160
111
161
private List<String > next (String s , String s2 ) {
112
- int i = 0 ;
113
- int n = s. length();
114
- for (; i < n && s. charAt(i) == s2. charAt(i); ++ i)
115
- ;
162
+ int i = 0 , n = s. length();
116
163
char [] cs = s. toCharArray();
164
+ for (; cs[i] == s2. charAt(i); ++ i) {
165
+ }
166
+
167
+ List<String > res = new ArrayList<> ();
168
+ for (int j = i + 1 ; j < n; ++ j) {
169
+ if (cs[j] == s2. charAt(i) && cs[j] != s2. charAt(j)) {
170
+ swap(cs, i, j);
171
+ res. add(new String (cs));
172
+ swap(cs, i, j);
173
+ }
174
+ }
175
+ return res;
176
+ }
177
+
178
+ private void swap (char [] cs , int i , int j ) {
179
+ char t = cs[i];
180
+ cs[i] = cs[j];
181
+ cs[j] = t;
182
+ }
183
+ }
184
+ ```
185
+
186
+ ``` java
187
+ class Solution {
188
+ public int kSimilarity (String s1 , String s2 ) {
189
+ PriorityQueue<Pair<Integer , String > > q = new PriorityQueue<> (Comparator . comparingInt(Pair :: getKey));
190
+ q. offer(new Pair<> (f(s1, s2), s1));
191
+ Map<String , Integer > dist = new HashMap<> ();
192
+ dist. put(s1, 0 );
193
+ while (true ) {
194
+ String s = q. poll(). getValue();
195
+ if (s. equals(s2)) {
196
+ return dist. get(s);
197
+ }
198
+ for (String nxt : next(s, s2)) {
199
+ if (! dist. containsKey(nxt) || dist. get(nxt) > dist. get(s) + 1 ) {
200
+ dist. put(nxt, dist. get(s) + 1 );
201
+ q. offer(new Pair<> (dist. get(nxt) + f(nxt, s2), nxt));
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ private int f (String s , String s2 ) {
208
+ int cnt = 0 ;
209
+ for (int i = 0 ; i < s. length(); ++ i) {
210
+ if (s. charAt(i) != s2. charAt(i)) {
211
+ ++ cnt;
212
+ }
213
+ }
214
+ return (cnt + 1 ) >> 1 ;
215
+ }
216
+
217
+ private List<String > next (String s , String s2 ) {
218
+ int i = 0 , n = s. length();
219
+ char [] cs = s. toCharArray();
220
+ for (; cs[i] == s2. charAt(i); ++ i) {
221
+ }
222
+
117
223
List<String > res = new ArrayList<> ();
118
224
for (int j = i + 1 ; j < n; ++ j) {
119
225
if (cs[j] == s2. charAt(i) && cs[j] != s2. charAt(j)) {
@@ -139,15 +245,17 @@ class Solution {
139
245
class Solution {
140
246
public:
141
247
int kSimilarity(string s1, string s2) {
142
- queue<string > q {{s1}};
143
- unordered_set<string > vis {{s1}};
248
+ queue<string > q{{s1}};
249
+ unordered_set<string > vis{{s1}};
144
250
int ans = 0;
145
- while (!q.empty() ) {
251
+ while (1 ) {
146
252
for (int i = q.size(); i; --i) {
147
- s1 = q.front();
253
+ auto s = q.front();
148
254
q.pop();
149
- if (s1 == s2) return ans;
150
- for (string nxt : next(s1, s2)) {
255
+ if (s == s2) {
256
+ return ans;
257
+ }
258
+ for (auto& nxt : next(s, s2)) {
151
259
if (!vis.count(nxt)) {
152
260
vis.insert(nxt);
153
261
q.push(nxt);
@@ -156,13 +264,60 @@ public:
156
264
}
157
265
++ans;
158
266
}
159
- return -1;
160
267
}
161
268
162
269
vector<string> next(string& s, string& s2) {
163
270
int i = 0, n = s.size();
164
- for (; i < n && s[i] == s2[i]; ++i)
165
- ;
271
+ for (; s[i] == s2[i]; ++i) {}
272
+ vector<string> res;
273
+ for (int j = i + 1 ; j < n; ++j) {
274
+ if (s[j] == s2[i] && s[j] != s2[j]) {
275
+ swap (s[ i] , s[ j] );
276
+ res.push_back(s);
277
+ swap(s[ i] , s[ j] );
278
+ }
279
+ }
280
+ return res;
281
+ }
282
+ };
283
+ ```
284
+
285
+ ``` cpp
286
+ using pis = pair<int , string>;
287
+
288
+ class Solution {
289
+ public:
290
+ int kSimilarity(string s1, string s2) {
291
+ priority_queue<pis, vector<pis >, greater<pis >> q;
292
+ q.push({f(s1, s2), s1});
293
+ unordered_map<string, int> dist;
294
+ dist[ s1] = 0;
295
+ while (1) {
296
+ auto [ _ , s] = q.top();
297
+ q.pop();
298
+ if (s == s2) {
299
+ return dist[ s] ;
300
+ }
301
+ for (auto& nxt : next(s, s2)) {
302
+ if (!dist.count(nxt) || dist[ nxt] > dist[ s] + 1) {
303
+ dist[ nxt] = dist[ s] + 1;
304
+ q.push({dist[ nxt] + f(nxt, s2), nxt});
305
+ }
306
+ }
307
+ }
308
+ }
309
+
310
+ int f(string& s, string& s2) {
311
+ int cnt = 0;
312
+ for (int i = 0; i < s.size(); ++i) {
313
+ cnt += s[i] != s2[i];
314
+ }
315
+ return (cnt + 1 ) >> 1 ;
316
+ }
317
+
318
+ vector<string> next (string& s, string& s2) {
319
+ int i = 0, n = s.size();
320
+ for (; s[ i] == s2[ i] ; ++i) {}
166
321
vector<string > res;
167
322
for (int j = i + 1; j < n; ++j) {
168
323
if (s[ j] == s2[ i] && s[ j] != s2[ j] ) {
@@ -183,12 +338,11 @@ func kSimilarity(s1 string, s2 string) int {
183
338
next := func(s string) []string {
184
339
i := 0
185
340
res := []string{}
186
- for s[i] == s2[i] {
187
- i++
341
+ for ; s[i] == s2[i]; i++ {
188
342
}
189
343
for j := i + 1; j < len(s1); j++ {
190
344
if s[j] == s2[i] && s[j] != s2[j] {
191
- res = append (res, s[0 :i]+string (s[j])+s[i+1 :j]+string (s[i])+s[j+1 :])
345
+ res = append(res, s[:i]+string(s[j])+s[i+1:j]+string(s[i])+s[j+1:])
192
346
}
193
347
}
194
348
return res
@@ -197,14 +351,14 @@ func kSimilarity(s1 string, s2 string) int {
197
351
q := []string{s1}
198
352
vis := map[string]bool{s1: true}
199
353
ans := 0
200
- for len (q) > 0 {
354
+ for {
201
355
for i := len(q); i > 0; i-- {
202
- s1 = q[0 ]
356
+ s : = q[0]
203
357
q = q[1:]
204
- if s1 == s2 {
358
+ if s == s2 {
205
359
return ans
206
360
}
207
- for _ , nxt := range next (s1 ) {
361
+ for _, nxt := range next(s ) {
208
362
if !vis[nxt] {
209
363
vis[nxt] = true
210
364
q = append(q, nxt)
@@ -213,10 +367,66 @@ func kSimilarity(s1 string, s2 string) int {
213
367
}
214
368
ans++
215
369
}
216
- return -1
217
370
}
218
371
```
219
372
373
+ ``` go
374
+ func kSimilarity (s1 string , s2 string ) int {
375
+ next := func (s string ) []string {
376
+ i := 0
377
+ res := []string {}
378
+ for ; s[i] == s2[i]; i++ {
379
+ }
380
+ for j := i + 1 ; j < len (s1); j++ {
381
+ if s[j] == s2[i] && s[j] != s2[j] {
382
+ res = append (res, s[:i]+string (s[j])+s[i+1 :j]+string (s[i])+s[j+1 :])
383
+ }
384
+ }
385
+ return res
386
+ }
387
+
388
+ f := func (s string ) int {
389
+ cnt := 0
390
+ for i := range s {
391
+ if s[i] != s2[i] {
392
+ cnt++
393
+ }
394
+ }
395
+ return (cnt + 1 ) >> 1
396
+ }
397
+
398
+ q := hp{pair{f (s1), s1}}
399
+ dist := map [string ]int {s1: 0 }
400
+ for {
401
+ s := heap.Pop (&q).(pair).s
402
+ if s == s2 {
403
+ return dist[s]
404
+ }
405
+ for _ , nxt := range next (s) {
406
+ if v , ok := dist[nxt]; !ok || v > dist[s]+1 {
407
+ dist[nxt] = dist[s] + 1
408
+ heap.Push (&q, pair{dist[nxt] + f (nxt), nxt})
409
+ }
410
+ }
411
+ }
412
+ }
413
+
414
+ type pair struct {
415
+ v int
416
+ s string
417
+ }
418
+ type hp []pair
419
+
420
+ func (h hp ) Len () int { return len (h) }
421
+ func (h hp ) Less (i , j int ) bool {
422
+ a , b := h[i], h[j]
423
+ return a.v < b.v
424
+ }
425
+ func (h hp ) Swap (i , j int ) { h[i], h[j] = h[j], h[i] }
426
+ func (h *hp ) Push (v interface {}) { *h = append (*h, v.(pair)) }
427
+ func (h *hp ) Pop () interface {} { a := *h; v := a[len (a)-1 ]; *h = a[:len (a)-1 ]; return v }
428
+ ```
429
+
220
430
### ** ...**
221
431
222
432
```
0 commit comments