Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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.
Example 1:
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:
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 <= 20s2.length == s1.lengths1 and s2 contain only lowercase letters from the set {'a', 'b', 'c', 'd', 'e', 'f'}.s2 is an anagram of s1.Problem summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map
"ab" "ba"
"abc" "bca"
couples-holding-hands)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #854: K-Similar Strings
class Solution {
public int kSimilarity(String s1, String s2) {
Deque<String> q = new ArrayDeque<>();
Set<String> vis = new HashSet<>();
q.offer(s1);
vis.add(s1);
int ans = 0;
while (true) {
for (int i = q.size(); i > 0; --i) {
String s = q.pollFirst();
if (s.equals(s2)) {
return ans;
}
for (String nxt : next(s, s2)) {
if (!vis.contains(nxt)) {
vis.add(nxt);
q.offer(nxt);
}
}
}
++ans;
}
}
private List<String> next(String s, String s2) {
int i = 0, n = s.length();
char[] cs = s.toCharArray();
for (; cs[i] == s2.charAt(i); ++i) {
}
List<String> res = new ArrayList<>();
for (int j = i + 1; j < n; ++j) {
if (cs[j] == s2.charAt(i) && cs[j] != s2.charAt(j)) {
swap(cs, i, j);
res.add(new String(cs));
swap(cs, i, j);
}
}
return res;
}
private void swap(char[] cs, int i, int j) {
char t = cs[i];
cs[i] = cs[j];
cs[j] = t;
}
}
// Accepted solution for LeetCode #854: K-Similar Strings
func kSimilarity(s1 string, s2 string) int {
next := func(s string) []string {
i := 0
res := []string{}
for ; s[i] == s2[i]; i++ {
}
for j := i + 1; j < len(s1); j++ {
if s[j] == s2[i] && s[j] != s2[j] {
res = append(res, s[:i]+string(s[j])+s[i+1:j]+string(s[i])+s[j+1:])
}
}
return res
}
q := []string{s1}
vis := map[string]bool{s1: true}
ans := 0
for {
for i := len(q); i > 0; i-- {
s := q[0]
q = q[1:]
if s == s2 {
return ans
}
for _, nxt := range next(s) {
if !vis[nxt] {
vis[nxt] = true
q = append(q, nxt)
}
}
}
ans++
}
}
# Accepted solution for LeetCode #854: K-Similar Strings
class Solution:
def kSimilarity(self, s1: str, s2: str) -> int:
def next(s):
i = 0
while s[i] == s2[i]:
i += 1
res = []
for j in range(i + 1, n):
if s[j] == s2[i] and s[j] != s2[j]:
res.append(s2[: i + 1] + s[i + 1 : j] + s[i] + s[j + 1 :])
return res
q = deque([s1])
vis = {s1}
ans, n = 0, len(s1)
while 1:
for _ in range(len(q)):
s = q.popleft()
if s == s2:
return ans
for nxt in next(s):
if nxt not in vis:
vis.add(nxt)
q.append(nxt)
ans += 1
// Accepted solution for LeetCode #854: K-Similar Strings
struct Solution;
use std::collections::HashSet;
use std::collections::VecDeque;
impl Solution {
fn k_similarity(a: String, b: String) -> i32 {
let n = a.len();
let a: Vec<char> = a.chars().collect();
let b: Vec<char> = b.chars().collect();
let mut queue = VecDeque::new();
let mut visited = HashSet::new();
visited.insert(a.clone());
queue.push_back(a);
let mut res = 0;
while !queue.is_empty() {
'outer: for _ in 0..queue.len() {
let mut front = queue.pop_front().unwrap();
let mut i = 0;
while i < n && front[i] == b[i] {
i += 1;
}
if i == n {
return res;
} else {
for j in i + 1..n {
if front[j] == b[i] && front[i] == b[j] {
front.swap(i, j);
if visited.insert(front.clone()) {
queue.push_back(front.clone());
}
front.swap(i, j);
continue 'outer;
}
}
for j in i + 1..n {
if front[j] == b[i] {
front.swap(i, j);
if visited.insert(front.clone()) {
queue.push_back(front.clone());
}
front.swap(i, j);
}
}
}
}
res += 1;
}
0
}
}
#[test]
fn test() {
let a = "ab".to_string();
let b = "ba".to_string();
let res = 1;
assert_eq!(Solution::k_similarity(a, b), res);
let a = "abc".to_string();
let b = "bca".to_string();
let res = 2;
assert_eq!(Solution::k_similarity(a, b), res);
let a = "abac".to_string();
let b = "baca".to_string();
let res = 2;
assert_eq!(Solution::k_similarity(a, b), res);
let a = "aabc".to_string();
let b = "abca".to_string();
let res = 2;
assert_eq!(Solution::k_similarity(a, b), res);
}
// Accepted solution for LeetCode #854: K-Similar Strings
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #854: K-Similar Strings
// class Solution {
// public int kSimilarity(String s1, String s2) {
// Deque<String> q = new ArrayDeque<>();
// Set<String> vis = new HashSet<>();
// q.offer(s1);
// vis.add(s1);
// int ans = 0;
// while (true) {
// for (int i = q.size(); i > 0; --i) {
// String s = q.pollFirst();
// if (s.equals(s2)) {
// return ans;
// }
// for (String nxt : next(s, s2)) {
// if (!vis.contains(nxt)) {
// vis.add(nxt);
// q.offer(nxt);
// }
// }
// }
// ++ans;
// }
// }
//
// private List<String> next(String s, String s2) {
// int i = 0, n = s.length();
// char[] cs = s.toCharArray();
// for (; cs[i] == s2.charAt(i); ++i) {
// }
//
// List<String> res = new ArrayList<>();
// for (int j = i + 1; j < n; ++j) {
// if (cs[j] == s2.charAt(i) && cs[j] != s2.charAt(j)) {
// swap(cs, i, j);
// res.add(new String(cs));
// swap(cs, i, j);
// }
// }
// return res;
// }
//
// private void swap(char[] cs, int i, int j) {
// char t = cs[i];
// cs[i] = cs[j];
// cs[j] = t;
// }
// }
Use this to step through a reusable interview workflow for this problem.
For each element, scan the rest of the array looking for a match. Two nested loops give n × (n−1)/2 comparisons = O(n²). No extra space since we only use loop indices.
One pass through the input, performing O(1) hash map lookups and insertions at each step. The hash map may store up to n entries in the worst case. This is the classic space-for-time tradeoff: O(n) extra memory eliminates an inner loop.
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.