Unique Substrings in Wraparound String
MedPrompt
We define the string base to be the infinite wraparound string of "abcdefghijklmnopqrstuvwxyz", so base will look like this:
"...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....".
Given a string s, return the number of unique non-empty substrings of s are present in base.
Example 1:
Input: s = "a"
Output: 1
Explanation: Only the substring "a" of s is in base.Example 2:
Input: s = "cac"
Output: 2
Explanation: There are two substrings ("a", "c") of s in base.Example 3:
Input: s = "zab"
Output: 6
Explanation: There are six substrings ("z", "a", "b", "za", "ab", and "zab") of s in base.
Constraints:
1 <= s.length <= 105sconsists of lowercase English letters.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves generating all possible substrings of the input string s. For each substring, we check if it is a valid 'wraparound' substring according to the problem definition. All unique valid substrings are stored in a HashSet to avoid duplicates. The final answer is the size of this set.
Algorithm
- Initialize an empty
HashSet<String>calleduniqueSubstrings. - Iterate through the string
swith an indexifrom0tos.length() - 1to define the start of a substring. - Start a nested loop with index
jfromitos.length() - 1. - In the inner loop, check if the character
s.charAt(j)followss.charAt(j-1)(forj > i). - If they are consecutive, the substring
s.substring(i, j+1)is valid. Add it touniqueSubstrings. - If they are not consecutive, break the inner loop, as any longer substring starting at
iwill also be invalid. - After the loops complete, return the size of
uniqueSubstrings.
Walkthrough
The core idea is to systematically explore every substring of s. We can use nested loops where the outer loop selects a starting index i, and the inner loop extends the substring to the right as long as it remains valid. A substring is valid if its characters are consecutive in the alphabet (e.g., 'a' -> 'b', 'z' -> 'a').
If a substring is found to be valid, it's added to a HashSet<String>. The HashSet automatically handles uniqueness, ensuring that we don't count the same substring multiple times. Finally, the number of unique valid substrings is simply the size of the set.
This method is straightforward but computationally expensive due to the large number of substrings and the overhead of string operations and storage.
import java.util.HashSet;import java.util.Set; class Solution { public int findSubstringInWraproundString(String s) { if (s == null || s.length() == 0) { return 0; } Set<String> uniqueSubstrings = new HashSet<>(); for (int i = 0; i < s.length(); i++) { // Add single character substring uniqueSubstrings.add(s.substring(i, i + 1)); for (int j = i + 1; j < s.length(); j++) { char prev = s.charAt(j - 1); char curr = s.charAt(j); // Check for wraparound consecutiveness if ((curr - prev + 26) % 26 == 1) { uniqueSubstrings.add(s.substring(i, j + 1)); } else { break; // End of consecutive sequence } } } return uniqueSubstrings.size(); }}Complexity
Time
O(N^3), where N is the length of the string `s`. The two nested loops give a factor of `O(N^2)`. Inside the loop, creating and hashing a substring of length `L` takes `O(L)` time. Since `L` can be up to `N`, the total time is `O(N^3)`.
Space
O(N^3), where N is the length of `s`. In the worst case (e.g., `s` is `"abc..."`), the set would store `O(N^2)` unique substrings, and the total space required for these strings can be up to `O(N^3)`.
Trade-offs
Pros
Simple to understand and implement.
Directly follows the problem statement by generating, validating, and counting unique substrings.
Cons
Highly inefficient. Time complexity is at least
O(N^2)due to nested loops, and can be up toO(N^3)because of string manipulation (substring creation and hashing).High space complexity, as the
HashSetmight need to storeO(N^2)substrings, leading toO(N^3)space usage in the worst case.Will result in a 'Time Limit Exceeded' (TLE) error for the given constraints (
N <= 10^5).
Solutions
Solution
class Solution {public int findSubstringInWraproundString(String p) { int[] dp = new int[26]; int k = 0; for (int i = 0; i < p.length(); ++i) { char c = p.charAt(i); if (i > 0 && (c - p.charAt(i - 1) + 26) % 26 == 1) { ++k; } else { k = 1; } dp[c - 'a'] = Math.max(dp[c - 'a'], k); } int ans = 0; for (int v : dp) { ans += v; } return ans; }}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.