Skip to content

Commit 39d75c7

Browse files
committedSep 20, 2022
feat: add solutions to lc problems: No.0854
No.0854.K-Similar Strings
1 parent bf078d2 commit 39d75c7

File tree

9 files changed

+485
-91
lines changed

9 files changed

+485
-91
lines changed
 

‎solution/0600-0699/0698.Partition to K Equal Sum Subsets/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class Solution {
191191
if (f[state] != 0) {
192192
return f[state] == 1;
193193
}
194-
for (int i = 0; i < nums.length; ++i) {
194+
for (int i = 0; i < n; ++i) {
195195
if (((state >> i) & 1) == 1) {
196196
continue;
197197
}

‎solution/0600-0699/0698.Partition to K Equal Sum Subsets/README_EN.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class Solution {
154154
if (f[state] != 0) {
155155
return f[state] == 1;
156156
}
157-
for (int i = 0; i < nums.length; ++i) {
157+
for (int i = 0; i < n; ++i) {
158158
if (((state >> i) & 1) == 1) {
159159
continue;
160160
}

‎solution/0800-0899/0854.K-Similar Strings/README.md

+240-30
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,28 @@
4343

4444
**方法一:BFS**
4545

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+
4668
<!-- tabs:start -->
4769

4870
### **Python3**
@@ -54,18 +76,18 @@ class Solution:
5476
def kSimilarity(self, s1: str, s2: str) -> int:
5577
def next(s):
5678
i = 0
57-
res = []
5879
while s[i] == s2[i]:
5980
i += 1
81+
res = []
6082
for j in range(i + 1, n):
6183
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 :])
6385
return res
6486

6587
q = deque([s1])
6688
vis = {s1}
6789
ans, n = 0, len(s1)
68-
while q:
90+
while 1:
6991
for _ in range(len(q)):
7092
s = q.popleft()
7193
if s == s2:
@@ -75,7 +97,36 @@ class Solution:
7597
vis.add(nxt)
7698
q.append(nxt)
7799
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))
79130
```
80131

81132
### **Java**
@@ -90,13 +141,13 @@ class Solution {
90141
q.offer(s1);
91142
vis.add(s1);
92143
int ans = 0;
93-
while (!q.isEmpty()) {
144+
while (true) {
94145
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)) {
97148
return ans;
98149
}
99-
for (String nxt : next(s1, s2)) {
150+
for (String nxt : next(s, s2)) {
100151
if (!vis.contains(nxt)) {
101152
vis.add(nxt);
102153
q.offer(nxt);
@@ -105,15 +156,70 @@ class Solution {
105156
}
106157
++ans;
107158
}
108-
return -1;
109159
}
110160

111161
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();
116163
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+
117223
List<String> res = new ArrayList<>();
118224
for (int j = i + 1; j < n; ++j) {
119225
if (cs[j] == s2.charAt(i) && cs[j] != s2.charAt(j)) {
@@ -139,15 +245,17 @@ class Solution {
139245
class Solution {
140246
public:
141247
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}};
144250
int ans = 0;
145-
while (!q.empty()) {
251+
while (1) {
146252
for (int i = q.size(); i; --i) {
147-
s1 = q.front();
253+
auto s = q.front();
148254
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)) {
151259
if (!vis.count(nxt)) {
152260
vis.insert(nxt);
153261
q.push(nxt);
@@ -156,13 +264,60 @@ public:
156264
}
157265
++ans;
158266
}
159-
return -1;
160267
}
161268

162269
vector<string> next(string& s, string& s2) {
163270
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) {}
166321
vector<string> res;
167322
for (int j = i + 1; j < n; ++j) {
168323
if (s[j] == s2[i] && s[j] != s2[j]) {
@@ -183,12 +338,11 @@ func kSimilarity(s1 string, s2 string) int {
183338
next := func(s string) []string {
184339
i := 0
185340
res := []string{}
186-
for s[i] == s2[i] {
187-
i++
341+
for ; s[i] == s2[i]; i++ {
188342
}
189343
for j := i + 1; j < len(s1); j++ {
190344
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:])
192346
}
193347
}
194348
return res
@@ -197,14 +351,14 @@ func kSimilarity(s1 string, s2 string) int {
197351
q := []string{s1}
198352
vis := map[string]bool{s1: true}
199353
ans := 0
200-
for len(q) > 0 {
354+
for {
201355
for i := len(q); i > 0; i-- {
202-
s1 = q[0]
356+
s := q[0]
203357
q = q[1:]
204-
if s1 == s2 {
358+
if s == s2 {
205359
return ans
206360
}
207-
for _, nxt := range next(s1) {
361+
for _, nxt := range next(s) {
208362
if !vis[nxt] {
209363
vis[nxt] = true
210364
q = append(q, nxt)
@@ -213,10 +367,66 @@ func kSimilarity(s1 string, s2 string) int {
213367
}
214368
ans++
215369
}
216-
return -1
217370
}
218371
```
219372

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+
220430
### **...**
221431

222432
```

0 commit comments

Comments
 (0)
Please sign in to comment.