Longest Uncommon Subsequence I
EasyPrompt
Given two strings a and b, return the length of the longest uncommon subsequence between a and b. If no such uncommon subsequence exists, return -1.
An uncommon subsequence between two strings is a string that is a subsequence of exactly one of them.
Example 1:
Input: a = "aba", b = "cdc"
Output: 3
Explanation: One longest uncommon subsequence is "aba" because "aba" is a subsequence of "aba" but not "cdc".
Note that "cdc" is also a longest uncommon subsequence.Example 2:
Input: a = "aaa", b = "bbb"
Output: 3
Explanation: The longest uncommon subsequences are "aaa" and "bbb".Example 3:
-1
Constraints:
1 <= a.length, b.length <= 100aandbconsist of lower-case English letters.
Approaches
3 approaches with complexity analysis and trade-offs.
This approach involves generating every possible subsequence for both strings, a and b. We can store these subsequences in two separate sets to handle duplicates. Then, we iterate through each set and find a subsequence that does not exist in the other set. The length of the longest such subsequence is our answer.
Algorithm
- Create two hash sets,
setAandsetB, to store the subsequences of stringsaandb. - Implement a recursive helper function
generateSubsequences(String s, int index, StringBuilder current, Set<String> set)to find all 2^n - 1 non-empty subsequences ofsand add them to the given set. - Call this function for both
aandbto populatesetAandsetB. - Initialize a variable
maxLengthto -1. - Iterate through each subsequence
subinsetA. Ifsubis not present insetB, updatemaxLength = Math.max(maxLength, sub.length()). - Iterate through each subsequence
subinsetB. Ifsubis not present insetA, updatemaxLength = Math.max(maxLength, sub.length()). - Return
maxLength.
Walkthrough
This method is a direct, albeit naive, translation of the problem's definition into code. It exhaustively generates all subsequences for both input strings and then compares the resulting sets of subsequences to find one that is unique to its original string. The longest such unique subsequence's length is the result.
import java.util.HashSet;import java.util.Set; class Solution { public int findLUSlength(String a, String b) { Set<String> setA = new HashSet<>(); generateSubsequences(a, 0, new StringBuilder(), setA); Set<String> setB = new HashSet<>(); generateSubsequences(b, 0, new StringBuilder(), setB); int maxLength = -1; for (String s : setA) { if (!setB.contains(s)) { maxLength = Math.max(maxLength, s.length()); } } for (String s : setB) { if (!setA.contains(s)) { maxLength = Math.max(maxLength, s.length()); } } return maxLength; } private void generateSubsequences(String s, int index, StringBuilder current, Set<String> set) { if (index == s.length()) { if (current.length() > 0) { set.add(current.toString()); } return; } // Case 1: Exclude the character at the current index generateSubsequences(s, index + 1, current, set); // Case 2: Include the character at the current index current.append(s.charAt(index)); generateSubsequences(s, index + 1, current, set); current.deleteCharAt(current.length() - 1); // Backtrack }}Complexity
Time
O(L_a * 2^|a| + L_b * 2^|b|). Generating all subsequences for a string of length n takes exponential time. For each string, there are 2^n subsequences, and creating/hashing them takes time proportional to their lengths.
Space
O(L_a * 2^|a| + L_b * 2^|b|), where |s| is the length of string s and L_s is the total length of all subsequences of s. This is because we need to store all 2^n subsequences for each string.
Trade-offs
Pros
It's a straightforward implementation based on the problem definition.
Guaranteed to be correct if it could run within time and memory limits.
Cons
Extremely inefficient in both time and space.
Not feasible for the given constraints and will result in a 'Time Limit Exceeded' or 'Memory Limit Exceeded' error.
This approach improves on the first one by avoiding the massive space complexity. Instead of generating and storing all subsequences beforehand, we generate subsequences for one string and, for each one, immediately check if it's also a subsequence of the other string. This avoids storing the entire, potentially huge, set of subsequences.
Algorithm
- Initialize
maxLength = -1. - Write a recursive function
generateAndCheckthat generates all subsequences of asourcestring. - Inside this function, for each generated subsequence, immediately check if it is a subsequence of the
targetstring using a helper functionisSubsequence(). - If it is NOT a subsequence of
target, updatemaxLengthwith the length of the current subsequence if it's greater. - Call
generateAndChecktwice: once withaas the source andbas the target, and once withbas the source andaas the target. - The
isSubsequence(sub, text)helper can be implemented with two pointers in O(|text|) time. - Return the final
maxLength.
Walkthrough
While this method is more space-efficient, it remains computationally expensive. We generate every subsequence of a and check its validity against b, then do the same for subsequences of b against a. The check for whether one string is a subsequence of another is efficient, but performing this check for an exponential number of subsequences is the bottleneck.
class Solution { private int maxLength = -1; public int findLUSlength(String a, String b) { // Check subsequences of 'a' against 'b' generateAndCheck(a, 0, new StringBuilder(), b); // Check subsequences of 'b' against 'a' generateAndCheck(b, 0, new StringBuilder(), a); return maxLength; } private void generateAndCheck(String source, int index, StringBuilder current, String target) { if (index == source.length()) { if (current.length() > 0) { if (!isSubsequence(current.toString(), target)) { maxLength = Math.max(maxLength, current.length()); } } return; } // Exclude current character generateAndCheck(source, index + 1, current, target); // Include current character current.append(source.charAt(index)); generateAndCheck(source, index + 1, current, target); current.deleteCharAt(current.length() - 1); // backtrack } private boolean isSubsequence(String s1, String s2) { int i = 0, j = 0; while (i < s1.length() && j < s2.length()) { if (s1.charAt(i) == s2.charAt(j)) { i++; } j++; } return i == s1.length(); }}Complexity
Time
O(|b| * 2^|a| + |a| * 2^|b|). For each of the 2^|a| subsequences of `a`, we perform a check against `b` which takes O(|b|) time. The same logic applies for string `b`.
Space
O(max(|a|, |b|)). The space is dominated by the recursion depth for generating subsequences and the `StringBuilder` used to construct them.
Trade-offs
Pros
More space-efficient than the first brute-force approach.
Still follows the problem definition logically.
Cons
The time complexity is still exponential, making it too slow for the given constraints.
It will receive a 'Time Limit Exceeded' verdict on most platforms.
This problem, despite its phrasing, can be solved with a very simple logical deduction. The key insight is to consider the strings a and b themselves as potential subsequences. This observation allows us to bypass any complex subsequence generation or checking, leading to a highly efficient solution.
Algorithm
- Compare string
aand stringbfor equality. - If
a.equals(b), it means any subsequence ofais also a subsequence ofb, and vice versa. No uncommon subsequence can exist. Return-1. - If
aandbare not equal, the longer of the two strings is guaranteed to be an uncommon subsequence. This is because a string is always a subsequence of itself, and it cannot be a subsequence of a shorter string. If the lengths are equal, since the strings are different, one cannot be a subsequence of the other. Therefore, the answer is the length of the longer string. - Return
Math.max(a.length(), b.length()).
Walkthrough
The core logic relies on analyzing two simple cases:
-
Strings are identical (
a.equals(b)): If the strings are the same, any subsequence ofais also a subsequence ofb. No uncommon subsequence exists. The result must be-1. -
Strings are different (
!a.equals(b)): In this scenario, the longer of the two strings is a guaranteed uncommon subsequence.- If
a.length() > b.length(),ais a subsequence of itself but cannot be a subsequence of the shorter stringb. Thus,ais an uncommon subsequence. No subsequence can be longer thanaitself, so the LUS length isa.length(). - If
a.length() == b.length(), sinceaandbare not equal,acannot be a subsequence ofb. Again,ais an uncommon subsequence of lengtha.length(), which is the maximum possible.
- If
This simplifies the problem to a single comparison and a length check.
class Solution { public int findLUSlength(String a, String b) { // If the strings are identical, any subsequence of one is a subsequence of the other. // No uncommon subsequence exists. if (a.equals(b)) { return -1; } // If the strings are different, the longer string is an uncommon subsequence. // A string is a subsequence of itself. // The longer string cannot be a subsequence of the shorter string. // If lengths are equal but strings are different, one cannot be a subsequence of the other. // Therefore, the longer string is a valid uncommon subsequence, and no longer one can exist. return Math.max(a.length(), b.length()); }}Complexity
Time
O(min(|a|, |b|)). The `a.equals(b)` method compares characters up to the length of the shorter string or until a mismatch is found. `String.length()` is an O(1) operation.
Space
O(1). No extra space is used beyond the storage for the input strings.
Trade-offs
Pros
Extremely efficient with linear time and constant space complexity.
Simple and elegant implementation.
Correctly identifies the trick/logical nature of the problem.
Cons
The simplicity of the solution can be non-obvious, as the problem statement might mislead one into thinking about more complex subsequence algorithms.
Solutions
Solution
class Solution {public int findLUSlength(String a, String b) { return a.equals(b) ? -1 : Math.max(a.length(), b.length()); }}class Solution {public: int findLUSlength(string a, string b) { return a == b ? -1 : max(a.size(), b.size()); }};class Solution: def findLUSlength(self, a: str, b: str) -> int: return - \ 1 if a == b else max(len(a), len(b))Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.