Problem#
Strings s1
and s2
are k
-similar (for some non-negative integer k
) if we can swap the positions of two letters in s1
exactly k
times so that the resulting string equals s2
.
Given two anagrams s1
and s2
, return the smallest k
for which s1
and
s2
are k
-similar .
Examples#
Example 1#
1
2
3
Input: s1 = "ab" , s2 = "ba"
Output: 1
Explanation: The two string are 1 - similar because we can use one swap to change s1 to s2: "ab" --> "ba" .
Example 2#
1
2
3
Input: s1 = "abc" , s2 = "bca"
Output: 2
Explanation: The two strings are 2 - similar because we can use two swaps to change s1 to s2: "abc" --> "bac" --> "bca" .
Constraints#
1 <= s1.length <= 20
s2.length == s1.length
s1
and s2
contain only lowercase letters from the set {'a', 'b', 'c', 'd', 'e', 'f'}
.
s2
is an anagram of s1
.
Solution#
Method 1 – Breadth-First Search (BFS) with Pruning#
Intuition#
To transform s1 into s2 with the minimum number of swaps, we can use BFS to explore all possible swaps, always swapping the first mismatched character with a character that matches s2 at that position. This ensures we only make swaps that bring s1 closer to s2, minimizing unnecessary moves.
Approach#
Use BFS starting from s1, keeping track of the number of swaps (steps).
At each step, find the first index i where the current string differs from s2.
For every j > i where current[j] == s2[i] and current[j] != s2[j], swap i and j to create a new string.
Add the new string to the queue if not visited.
Repeat until s1 equals s2.
Return the number of swaps taken.
Code#
Cpp
Go
Java
Kotlin
Python
Rust
Typescript
1
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
class Solution {
public :
int kSimilarity(string s1, string s2) {
queue< pair< string, int >> q;
unordered_set< string> vis;
q.push({s1, 0 });
vis.insert(s1);
while (! q.empty()) {
auto [cur, step] = q.front(); q.pop();
if (cur == s2) return step;
int i = 0 ;
while (cur[i] == s2[i]) ++ i;
for (int j = i + 1 ; j < cur.size(); ++ j) {
if (cur[j] == s2[i] && cur[j] != s2[j]) {
string nxt = cur;
swap(nxt[i], nxt[j]);
if (! vis.count(nxt)) {
vis.insert(nxt);
q.push({nxt, step + 1 });
}
}
}
}
return - 1 ;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func kSimilarity (s1 , s2 string ) int {
type pair struct { s string ; step int }
q := []pair {{s1 , 0 }}
vis := map [string ]bool {s1 : true }
for len(q ) > 0 {
cur , step := q [0 ].s , q [0 ].step
q = q [1 :]
if cur == s2 { return step }
i := 0
for i < len(cur ) && cur [i ] == s2 [i ] { i ++ }
for j := i + 1 ; j < len(cur ); j ++ {
if cur [j ] == s2 [i ] && cur [j ] != s2 [j ] {
nxt := []byte(cur )
nxt [i ], nxt [j ] = nxt [j ], nxt [i ]
ns := string(nxt )
if !vis [ns ] {
vis [ns ] = true
q = append(q , pair {ns , step + 1 })
}
}
}
}
return - 1
}
1
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
class Solution {
public int kSimilarity (String s1, String s2) {
Queue< Pair< String, Integer>> q = new LinkedList<> ();
Set< String> vis = new HashSet<> ();
q.offer (new Pair<> (s1, 0));
vis.add (s1);
while (! q.isEmpty ()) {
Pair< String, Integer> p = q.poll ();
String cur = p.getKey ();
int step = p.getValue ();
if (cur.equals (s2)) return step;
int i = 0;
while (cur.charAt (i) == s2.charAt (i)) i++ ;
for (int j = i + 1; j < cur.length (); j++ ) {
if (cur.charAt (j) == s2.charAt (i) && cur.charAt (j) != s2.charAt (j)) {
char [] arr = cur.toCharArray ();
char tmp = arr[ i] ; arr[ i] = arr[ j] ; arr[ j] = tmp;
String nxt = new String(arr);
if (! vis.contains (nxt)) {
vis.add (nxt);
q.offer (new Pair<> (nxt, step + 1));
}
}
}
}
return - 1;
}
}
1
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
class Solution {
fun kSimilarity (s1: String, s2: String): Int {
data class State (val s: String, val step: Int)
val q = ArrayDeque<State>()
val vis = mutableSetOf<String>()
q.add(State(s1, 0 ))
vis.add(s1)
while (q.isNotEmpty()) {
val ( cur, step) = q.removeFirst()
if (cur == s2) return step
var i = 0
while (cur[i] == s2[i]) i++
for (j in i+1 until cur.length) {
if (cur[j] == s2[i] && cur[j] != s2[j]) {
val arr = cur.toCharArray()
arr[i] = arr[j].also { arr[j] = arr[i] }
val nxt = String(arr)
if (nxt !in vis) {
vis.add(nxt)
q.add(State(nxt, step + 1 ))
}
}
}
}
return -1
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution :
def kSimilarity (self, s1: str, s2: str) -> int:
from collections import deque
q = deque([(s1, 0 )])
vis = {s1}
while q:
cur, step = q. popleft()
if cur == s2:
return step
i = 0
while cur[i] == s2[i]:
i += 1
for j in range(i+ 1 , len(cur)):
if cur[j] == s2[i] and cur[j] != s2[j]:
nxt = list(cur)
nxt[i], nxt[j] = nxt[j], nxt[i]
nxt_str = '' . join(nxt)
if nxt_str not in vis:
vis. add(nxt_str)
q. append((nxt_str, step+ 1 ))
return - 1
1
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
use std::collections::{HashSet, VecDeque};
impl Solution {
pub fn k_similarity (s1: String, s2: String) -> i32 {
let mut q = VecDeque::new();
let mut vis = HashSet::new();
q.push_back((s1.clone(), 0 ));
vis.insert(s1.clone());
while let Some((cur, step)) = q.pop_front() {
if cur == s2 { return step; }
let cur_bytes = cur.as_bytes();
let s2_bytes = s2.as_bytes();
let mut i = 0 ;
while cur_bytes[i] == s2_bytes[i] { i += 1 ; }
for j in i+ 1 .. cur.len() {
if cur_bytes[j] == s2_bytes[i] && cur_bytes[j] != s2_bytes[j] {
let mut nxt = cur_bytes.to_vec();
nxt.swap(i, j);
let nxt_str = String::from_utf8(nxt).unwrap();
if ! vis.contains(& nxt_str) {
vis.insert(nxt_str.clone());
q.push_back((nxt_str, step+ 1 ));
}
}
}
}
- 1
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
kSimilarity (s1 : string , s2 : string ): number {
const q : [string , number ][] = [[s1 , 0 ]];
const vis = new Set <string >([s1 ]);
while (q .length ) {
const [cur , step ] = q .shift ()! ;
if (cur === s2 ) return step ;
let i = 0 ;
while (cur [i ] === s2 [i ]) i ++ ;
for (let j = i + 1 ; j < cur .length ; ++ j ) {
if (cur [j ] === s2 [i ] && cur [j ] !== s2 [j ]) {
const arr = cur .split ('' );
[arr [i ], arr [j ]] = [arr [j ], arr [i ]];
const nxt = arr .join ('' );
if (! vis .has (nxt )) {
vis .add (nxt );
q .push ([nxt , step + 1 ]);
}
}
}
}
return - 1 ;
}
}
Complexity#
⏰ Time complexity: O(n! * n)
, where n is the length of the string. In the worst case, we may explore all permutations, but pruning reduces the search space.
🧺 Space complexity: O(n! * n)
, for the queue and visited set.