Longest Alternating Hamming Subsequence

 Longest Alternating Hamming Subsequence

Problem Statement

You're given:

  • An array of distinct strings words[]

  • An array groups[] (only 0s and 1s), same length as words

You need to find the longest subsequence of words such that:

✅ Consecutive elements are from different groups
✅ Words are of the same length
✅ And differ by exactly one character (i.e., Hamming Distance = 1)

Return any one such longest subsequence.

Example

Input:

words = ["cold", "cord", "card", "ward", "warm"] groups = [1, 0, 1, 0, 1]

Output:

["cold", "cord", "card", "ward", "warm"]

Best Data Structure for the Job

  • Arrays and Lists 

  • No need for fancy trees or maps

  • Just a good old DP array and a prev[] tracker to rebuild the subsequence 

Different Approaches

1)Brute Force — Try All Subsequences (Exponential Time)

class Solution { public List<String> getWordsInLongestSubsequence(String[] words, int[] groups) { int n = words.length; List<String> best = new ArrayList<>(); int total = 1 << n; // All subsequence combos for (int mask = 1; mask < total; mask++) { List<String> current = new ArrayList<>(); int lastGroup = -1; String lastWord = ""; boolean valid = true; for (int i = 0; i < n; i++) { if ((mask & (1 << i)) != 0) { if (!current.isEmpty()) { if (groups[i] == lastGroup || words[i].length() != lastWord.length() || hamming(words[i], lastWord) != 1) { valid = false; break; } } current.add(words[i]); lastGroup = groups[i]; lastWord = words[i]; } } if (valid && current.size() > best.size()) { best = new ArrayList<>(current); } } return best; } private int hamming(String a, String b) { int dist = 0; for (int i = 0; i < a.length(); i++) { if (a.charAt(i) != b.charAt(i)) dist++; } return dist; } }

2)Dynamic Programming 

Smarter approach. Build dp[i] as the length of the longest valid subsequence ending at index i.

class Solution { public List<String> getWordsInLongestSubsequence(String[] words, int[] groups) { int n = words.length; int[] dp = new int[n]; int[] prev = new int[n]; Arrays.fill(dp, 1); Arrays.fill(prev, -1); int maxLen = 1; int maxIndex = 0; for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (groups[i] != groups[j] && words[i].length() == words[j].length() && hamming(words[i], words[j]) == 1 && dp[j] + 1 > dp[i]) { dp[i] = dp[j] + 1; prev[i] = j; } } if (dp[i] > maxLen) { maxLen = dp[i]; maxIndex = i; } } List<String> result = new ArrayList<>(); int curr = maxIndex; while (curr != -1) { result.add(words[curr]); curr = prev[curr]; } Collections.reverse(result); return result; } private int hamming(String a, String b) { int dist = 0; for (int i = 0; i < a.length(); i++) { if (a.charAt(i) != b.charAt(i)) dist++; } return dist; } }

⚠️ Common Pitfalls

  • Forgetting subsequences can skip elements (this ain’t a substring prob)

  • Not checking for group alternation

  • Miscomputing Hamming distance — don't count mismatched lengths!

📊 Complexity Summary

Approach        Time Complexity        Space Complexity
Brute Force        O(2ⁿ × n)        O(n)
Dynamic Programming        O(n²)        O(n)

🌍 Real-World Use Cases

  • Scheduling alternating shifts (e.g., Day/Night staff)

  • Mutation tracking (minimum genetic mutation)

  • Game design: alternating turns or player types

  • Network signal pattern detection


I hope you found this article insightful! Keep exploring, keep learning, and don’t forget to check back daily for more exciting problem breakdowns. If you know someone who would benefit from this, share it with them and help them grow too! That’s it for today—see you in the next post!

                                                                                                                                         Signing off!! 

                                                                                                Master DSA One Problem at a Time :)

Comments

Popular posts from this blog

Trailing Zeros in Factorial – How Many and Why?

Divisible Drama — Who’s In, Who’s Out?

Zero Array After Queries