2014. Longest Subsequence Repeated k Times
Problem Description
You are given a string s
of length n
and an integer k
. Your task is to find the longest subsequence that can be repeated k
times in string s
.
A subsequence is a string that can be derived from another string by deleting some or no characters without changing the order of the remaining characters.
A subsequence seq
is considered repeated k
times in string s
if seq * k
(which means concatenating seq
with itself k
times) is a subsequence of s
.
For example, consider the string "bababcba"
and the subsequence "bba"
:
- If we concatenate
"bba"
2 times, we get"bbabba"
- We can find
"bbabba"
as a subsequence in"bababcba"
by selecting characters at positions:b
(0)b
(2)ab
(3,4)ba
(6,7) - Therefore,
"bba"
is repeated 2 times in the string
The goal is to return the longest subsequence that can be repeated k
times. If there are multiple subsequences of the same maximum length, return the lexicographically largest one. If no such subsequence exists, return an empty string.
The solution uses BFS to explore all possible subsequences. It first identifies characters that appear at least k
times in the string (since any character in the repeated subsequence must appear at least k
times). Then it builds subsequences incrementally, checking each candidate to see if it can be repeated k
times within the original string. The check
function verifies whether a given subsequence can be found k
times by using a two-pointer approach to match characters sequentially through the string.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- No: The problem involves finding subsequences in a string, not nodes and edges as in graph problems.
Need to solve for kth smallest/largest?
- No: We're looking for the longest/lexicographically largest subsequence that can be repeated k times, not the kth smallest or largest element.
Involves Linked Lists?
- No: The problem works with strings and subsequences, not linked list data structures.
Does the problem have small constraints?
- Yes: Looking at the problem, we need to explore all possible subsequences and check if they can be repeated k times. The fact that we're looking for subsequences (not substrings) and need to verify each one suggests the search space is manageable. Additionally, the solution uses BFS to enumerate all possible subsequences, which is feasible only with small constraints.
Brute force / Backtracking?
- Yes: The problem requires exploring all possible subsequences systematically. We build subsequences character by character, checking at each step whether the current subsequence can be repeated k times in the original string. This is a classic backtracking/brute force pattern where we:
- Start with an empty subsequence
- Try adding each valid character
- Check if the new subsequence is valid (can be repeated k times)
- Continue building longer subsequences or backtrack
Conclusion: The flowchart correctly identifies this as a backtracking/brute force problem. The solution implements this through BFS, where we systematically generate and test all possible subsequences, keeping track of the longest valid one that can be repeated k times.
Intuition
When we need to find the longest subsequence that can be repeated k
times, we face two key challenges: generating all possible subsequences and efficiently checking if each one can be repeated k
times.
The first insight is that any character appearing in our answer subsequence must appear at least k
times in the original string. Why? Because if we repeat the subsequence k
times, each character in it will appear k
times in the concatenated result. So we can immediately filter out characters that don't meet this frequency requirement.
The second insight is about how to systematically explore all possible subsequences. Since we want the longest one (and lexicographically largest if there are ties), we can use a level-by-level exploration approach. We start with an empty string and gradually build longer subsequences by appending one character at a time. This is where BFS shines - it naturally explores all subsequences of length 1, then length 2, and so on.
Why BFS instead of DFS? BFS guarantees that when we find a valid subsequence of length n
, we've already explored all subsequences of length n-1
. This means the last valid subsequence we find at each level is guaranteed to be one of the longest. Since we append characters in order from our filtered character set, we also ensure lexicographic ordering within each length.
The verification step is straightforward but crucial. For each candidate subsequence, we need to check if we can find it k
times in the original string. We do this by scanning through the string with two pointers - one for the original string and one for our subsequence pattern. Each time we complete matching the pattern, we decrement k
. If we can match the pattern k
times before reaching the end of the string, the subsequence is valid.
This approach elegantly combines the exhaustive search needed for finding all subsequences with the efficiency of pruning invalid candidates early, making it practical despite the potentially large search space.
Learn more about Greedy and Backtracking patterns.
Solution Approach
The solution implements a BFS-based approach to systematically explore all possible subsequences and find the longest one that can be repeated k
times.
Step 1: Character Filtering
First, we count the frequency of each character in the string using a Counter
. Then we filter out characters that appear less than k
times, storing valid characters in a list cs
:
cnt = Counter(s) cs = [c for c in ascii_lowercase if cnt[c] >= k]
This preprocessing step ensures we only consider characters that could possibly be part of a subsequence repeated k
times.
Step 2: BFS Initialization
We initialize a queue with an empty string and set up our answer variable:
q = deque([""]) ans = ""
The empty string serves as our starting point for building subsequences.
Step 3: Level-by-Level Exploration
We process the queue using BFS, where each level represents subsequences of a particular length:
while q: cur = q.popleft() for c in cs: nxt = cur + c if check(nxt, k): ans = nxt q.append(nxt)
For each subsequence cur
from the queue, we try appending each valid character c
to create a new subsequence nxt
. If nxt
can be repeated k
times (verified by the check
function), we update our answer and add nxt
to the queue for further extension.
Step 4: Verification Function
The check
function uses a two-pointer approach to verify if a subsequence t
can be found k
times in the string s
:
def check(t: str, k: int) -> bool:
i = 0
for c in s:
if c == t[i]:
i += 1
if i == len(t):
k -= 1
if k == 0:
return True
i = 0
return False
The function maintains pointer i
for the current position in pattern t
. As we scan through s
, whenever we find a matching character, we advance i
. When we complete matching the entire pattern (i == len(t)
), we decrement k
and reset i
to start matching the pattern again. If we successfully match the pattern k
times, we return True
.
Why This Works:
- BFS ensures we explore subsequences in increasing order of length, so the last valid subsequence we find is among the longest
- By iterating through characters in
cs
(which contains characters in alphabetical order), we naturally handle the lexicographic requirement - The two-pointer verification is efficient, running in
O(n)
time for each check - The overall approach guarantees finding the optimal solution by exhaustively checking all valid possibilities
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with s = "bababcba"
and k = 2
.
Step 1: Character Filtering First, count character frequencies:
- 'a': 3 times
- 'b': 4 times
- 'c': 1 time
Since we need characters that appear at least k=2
times, we filter to get cs = ['a', 'b']
.
Step 2: BFS Exploration
We start with queue = [""]
and begin exploring:
Level 0: Process ""
- Try appending 'a': Check if "a" can repeat 2 times
- Scan:
b[a]babcba
→ found 1st 'a' at index 1 - Continue:
bab[a]bcba
→ found 2nd 'a' at index 3 - ✓ Valid! Update
ans = "a"
, add to queue
- Scan:
- Try appending 'b': Check if "b" can repeat 2 times
- Scan:
[b]ababcba
→ found 1st 'b' at index 0 - Continue:
ba[b]abcba
→ found 2nd 'b' at index 2 - ✓ Valid! Update
ans = "b"
, add to queue
- Scan:
Queue now: ["a", "b"]
Level 1: Process "a"
- Try appending 'a': Check if "aa" can repeat 2 times (need "aaaa")
- Scan for 4 'a's total, but only have 3 'a's in string
- ✗ Invalid, skip
- Try appending 'b': Check if "ab" can repeat 2 times (need "abab")
- Scan:
b[a]babcba
→ found 1st 'a' at index 1 - Continue:
ba[b]abcba
→ found 1st 'b' at index 2 - Continue:
bab[a]bcba
→ found 2nd 'a' at index 3 - Continue:
baba[b]cba
→ found 2nd 'b' at index 4 - ✓ Valid! Update
ans = "ab"
, add to queue
- Scan:
Level 1 (continued): Process "b"
- Try appending 'a': Check if "ba" can repeat 2 times (need "baba")
- Scan:
[b]ababcba
→ found 1st 'b' at index 0 - Continue:
b[a]babcba
→ found 1st 'a' at index 1 - Continue:
ba[b]abcba
→ found 2nd 'b' at index 2 - Continue:
bab[a]bcba
→ found 2nd 'a' at index 3 - ✓ Valid! Update
ans = "ba"
, add to queue
- Scan:
- Try appending 'b': Check if "bb" can repeat 2 times (need "bbbb")
- Need 4 'b's in order, but can only find: b(0), b(2), b(4), b(6)
- ✓ Valid! Update
ans = "bb"
, add to queue
Queue now: ["ab", "ba", "bb"]
Level 2: Process "ab"
, "ba"
, "bb"
For "ab"
:
- Try "aba": Check if can repeat 2 times (need "abaaba")
- After checking: ✗ Cannot find this pattern twice
- Try "abb": Check if can repeat 2 times (need "abbabb")
- After checking: ✗ Cannot find this pattern twice
For "ba"
:
- Try "baa": ✗ Invalid
- Try "bab": ✗ Invalid
For "bb"
:
- Try "bba": Check if can repeat 2 times (need "bbabba")
- Scan:
[b]ababcba
→ 1st 'b' - Continue:
ba[b]abcba
→ 2nd 'b' - Continue:
bab[a]bcba
→ 1st 'a' - Continue:
baba[b]cba
→ 3rd 'b' - Continue:
babab[b]a
→ 4th 'b' - Continue:
bababcb[a]
→ 2nd 'a' - ✓ Valid! Update
ans = "bba"
- Scan:
- Try "bbb": ✗ Invalid
The process continues, but no subsequence longer than 3 characters can be repeated twice.
Final Answer: "bba"
- the longest subsequence that can be repeated 2 times in "bababcba".
Solution Implementation
1from collections import Counter, deque
2from string import ascii_lowercase
3
4class Solution:
5 def longestSubsequenceRepeatedK(self, s: str, k: int) -> str:
6 def is_valid_subsequence(pattern: str, required_count: int) -> bool:
7 """
8 Check if the pattern can be found as a subsequence in string s
9 at least required_count times.
10 """
11 pattern_index = 0
12 for char in s:
13 if char == pattern[pattern_index]:
14 pattern_index += 1
15 # If we've matched the entire pattern once
16 if pattern_index == len(pattern):
17 required_count -= 1
18 # If we've found the pattern k times, return True
19 if required_count == 0:
20 return True
21 # Reset to find the pattern again
22 pattern_index = 0
23 return False
24
25 # Count frequency of each character in the string
26 char_frequency = Counter(s)
27
28 # Only consider characters that appear at least k times
29 # (necessary condition for being in a subsequence repeated k times)
30 valid_chars = [char for char in ascii_lowercase if char_frequency[char] >= k]
31
32 # BFS queue to build subsequences incrementally
33 queue = deque([""])
34 result = ""
35
36 # BFS to find the longest valid subsequence
37 while queue:
38 current_subsequence = queue.popleft()
39
40 # Try extending the current subsequence with each valid character
41 for char in valid_chars:
42 next_subsequence = current_subsequence + char
43
44 # Check if this new subsequence can be repeated k times
45 if is_valid_subsequence(next_subsequence, k):
46 # Update result (BFS guarantees we find longest first)
47 result = next_subsequence
48 # Add to queue for further extension
49 queue.append(next_subsequence)
50
51 return result
52
1class Solution {
2 private char[] sourceArray;
3
4 /**
5 * Finds the longest subsequence that can be repeated k times in string s
6 * @param s the input string
7 * @param k the number of times the subsequence should repeat
8 * @return the lexicographically largest valid subsequence
9 */
10 public String longestSubsequenceRepeatedK(String s, int k) {
11 this.sourceArray = s.toCharArray();
12
13 // Count frequency of each character in the string
14 int[] charFrequency = new int[26];
15 for (char ch : this.sourceArray) {
16 charFrequency[ch - 'a']++;
17 }
18
19 // Collect characters that appear at least k times (potential candidates)
20 List<Character> validCharacters = new ArrayList<>();
21 for (char ch = 'a'; ch <= 'z'; ++ch) {
22 if (charFrequency[ch - 'a'] >= k) {
23 validCharacters.add(ch);
24 }
25 }
26
27 // BFS to find the longest valid subsequence
28 Deque<String> queue = new ArrayDeque<>();
29 queue.offer("");
30 String result = "";
31
32 while (!queue.isEmpty()) {
33 String currentString = queue.poll();
34
35 // Try appending each valid character to current string
36 for (char ch : validCharacters) {
37 String nextString = currentString + ch;
38
39 // Check if the new string can be repeated k times as subsequence
40 if (isValidSubsequence(nextString, k)) {
41 result = nextString; // Update result (BFS ensures longest)
42 queue.offer(nextString);
43 }
44 }
45 }
46
47 return result;
48 }
49
50 /**
51 * Checks if pattern can be found k times as a subsequence in sourceArray
52 * @param pattern the pattern to search for
53 * @param k the number of times pattern should appear
54 * @return true if pattern appears at least k times as subsequence
55 */
56 private boolean isValidSubsequence(String pattern, int k) {
57 int patternIndex = 0;
58
59 for (char ch : sourceArray) {
60 // Match current character with pattern
61 if (ch == pattern.charAt(patternIndex)) {
62 patternIndex++;
63
64 // Complete pattern match found
65 if (patternIndex == pattern.length()) {
66 k--;
67 if (k == 0) {
68 return true; // Found k occurrences
69 }
70 patternIndex = 0; // Reset to find next occurrence
71 }
72 }
73 }
74
75 return false;
76 }
77}
78
1class Solution {
2public:
3 string longestSubsequenceRepeatedK(string s, int k) {
4 // Lambda function to check if string pattern appears k times as subsequence in s
5 auto isValidSubsequence = [&](const string& pattern, int k) -> bool {
6 int patternIndex = 0;
7
8 // Iterate through the original string
9 for (char ch : s) {
10 // If current character matches the pattern character
11 if (ch == pattern[patternIndex]) {
12 patternIndex++;
13
14 // If we've matched the entire pattern once
15 if (patternIndex == pattern.size()) {
16 k--; // Decrement the required repetition count
17 if (k == 0) {
18 return true; // Found k repetitions
19 }
20 patternIndex = 0; // Reset to find next repetition
21 }
22 }
23 }
24 return false; // Couldn't find k repetitions
25 };
26
27 // Count frequency of each character in the string
28 int charFrequency[26] = {};
29 for (char ch : s) {
30 charFrequency[ch - 'a']++;
31 }
32
33 // Collect characters that appear at least k times (potential candidates)
34 vector<char> validChars;
35 for (char ch = 'a'; ch <= 'z'; ++ch) {
36 if (charFrequency[ch - 'a'] >= k) {
37 validChars.push_back(ch);
38 }
39 }
40
41 // BFS to build subsequences incrementally
42 queue<string> bfsQueue;
43 bfsQueue.push(""); // Start with empty string
44 string result;
45
46 // Process queue level by level
47 while (!bfsQueue.empty()) {
48 string currentPattern = bfsQueue.front();
49 bfsQueue.pop();
50
51 // Try extending current pattern with each valid character
52 for (char ch : validChars) {
53 string nextPattern = currentPattern + ch;
54
55 // Check if the new pattern can be repeated k times in s
56 if (isValidSubsequence(nextPattern, k)) {
57 result = nextPattern; // Update result (BFS ensures longest valid)
58 bfsQueue.push(nextPattern); // Add to queue for further extension
59 }
60 }
61 }
62
63 return result;
64 }
65};
66
1/**
2 * Finds the longest subsequence that can be repeated k times in string s
3 * @param s - The input string
4 * @param k - The number of times the subsequence should repeat
5 * @returns The longest valid subsequence as a string
6 */
7function longestSubsequenceRepeatedK(s: string, k: number): string {
8 /**
9 * Checks if a given pattern can be found as a subsequence k times in string s
10 * @param pattern - The pattern to search for
11 * @param requiredCount - Number of times the pattern should appear
12 * @returns True if pattern appears as subsequence at least requiredCount times
13 */
14 const checkPatternExists = (pattern: string, requiredCount: number): boolean => {
15 let patternIndex = 0;
16
17 // Iterate through each character in the original string
18 for (const char of s) {
19 // If current character matches the pattern character we're looking for
20 if (char === pattern[patternIndex]) {
21 patternIndex++;
22
23 // If we've matched the entire pattern once
24 if (patternIndex === pattern.length) {
25 requiredCount--;
26
27 // If we've found the pattern k times, return true
28 if (requiredCount === 0) {
29 return true;
30 }
31
32 // Reset to search for the pattern again
33 patternIndex = 0;
34 }
35 }
36 }
37
38 return false;
39 };
40
41 // Count frequency of each character (26 lowercase letters)
42 const charFrequency = new Array(26).fill(0);
43 for (const char of s) {
44 charFrequency[char.charCodeAt(0) - 97]++;
45 }
46
47 // Collect characters that appear at least k times
48 const validCharacters: string[] = [];
49 for (let i = 0; i < 26; i++) {
50 if (charFrequency[i] >= k) {
51 validCharacters.push(String.fromCharCode(97 + i));
52 }
53 }
54
55 // BFS queue to explore all possible subsequences
56 const queue: string[] = [''];
57 let longestValidSubsequence = '';
58
59 // BFS to find the longest valid subsequence
60 while (queue.length > 0) {
61 const currentSubsequence = queue.shift()!;
62
63 // Try extending current subsequence with each valid character
64 for (const char of validCharacters) {
65 const nextSubsequence = currentSubsequence + char;
66
67 // If the new subsequence can be repeated k times
68 if (checkPatternExists(nextSubsequence, k)) {
69 // Update the answer (BFS guarantees we find longest first)
70 longestValidSubsequence = nextSubsequence;
71 queue.push(nextSubsequence);
72 }
73 }
74 }
75
76 return longestValidSubsequence;
77}
78
Time and Space Complexity
Time Complexity: O(n * m * 2^m)
where n
is the length of string s
and m
is the number of distinct characters that appear at least k
times in s
.
- The algorithm uses BFS to explore all possible subsequences built from characters that appear at least
k
times - In the worst case, we generate all possible strings from
m
characters, which gives usO(2^m)
possible strings (each character can be included or not, multiple times) - For each generated string of length
l
, thecheck
function takesO(n)
time to verify if it's a valid subsequence repeatedk
times - The maximum length of any valid subsequence is bounded by
n/k
, but more tightly bym * (n/k)
considering character frequencies - The total number of strings generated is approximately
m + m^2 + m^3 + ... + m^(n/k) ≈ O(m^(n/k))
in the worst case, but practically bounded by valid subsequences - Since we're doing BFS level by level and only keeping valid subsequences, the effective complexity is
O(n * m * 2^m)
Space Complexity: O(m * 2^m)
- The queue
q
can contain at most all valid subsequences at any point - In the worst case, this could be all possible strings formed from
m
characters - Each string has maximum length
n/k
, but the number of strings dominates the space complexity - The counter and character list
cs
takeO(26) = O(1)
space for lowercase letters - Therefore, the overall space complexity is
O(m * 2^m)
for storing all possible valid subsequences in the queue
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Character Frequency Filtering
A common mistake is assuming that if a character appears exactly k
times, it can be part of the repeated subsequence. However, this is only a necessary condition, not sufficient.
Pitfall Example:
# Incorrect assumption valid_chars = [char for char in s if s.count(char) == k] # Wrong!
Why it's wrong: A character that appears k
times can only appear once in the subsequence pattern (not multiple times). If a character appears 2k
times, it could appear twice in the pattern.
Correct Approach:
# A character must appear AT LEAST k times to be in the pattern valid_chars = [char for char in ascii_lowercase if char_frequency[char] >= k]
2. Not Handling Lexicographic Order Properly
The problem asks for the lexicographically largest subsequence among those with maximum length. A common pitfall is trying to find this by sorting or by complex comparisons.
Pitfall Example:
# Trying to track all max-length subsequences and then sort
max_length_subsequences = []
# ... collect all subsequences of max length
return max(max_length_subsequences) # Inefficient and complex
Why BFS naturally handles this: Since we iterate through valid_chars
in alphabetical order and BFS explores level by level, the last valid subsequence we find at each length is automatically the lexicographically largest for that length.
3. Greedy Subsequence Verification
A critical mistake is using a greedy approach that doesn't properly reset when matching the pattern multiple times.
Pitfall Example:
def incorrect_check(pattern: str, k: int) -> bool:
matches = 0
pattern_idx = 0
for char in s:
if pattern_idx < len(pattern) and char == pattern[pattern_idx]:
pattern_idx += 1
if pattern_idx == len(pattern):
matches += 1
# Forgot to reset pattern_idx!
return matches >= k
Correct Implementation:
def is_valid_subsequence(pattern: str, required_count: int) -> bool:
pattern_index = 0
for char in s:
if char == pattern[pattern_index]:
pattern_index += 1
if pattern_index == len(pattern):
required_count -= 1
if required_count == 0:
return True
pattern_index = 0 # Critical: Reset to match pattern again
return False
4. Memory/Time Limit Issues with Large Inputs
BFS can explore an exponential number of subsequences. Without proper pruning, this can lead to TLE or MLE.
Pitfall Example:
# Adding all possible subsequences without checking validity first for char in valid_chars: next_subsequence = current + char queue.append(next_subsequence) # Adding without validation
Optimized Approach:
# Only add to queue if it's a valid k-repeated subsequence for char in valid_chars: next_subsequence = current + char if is_valid_subsequence(next_subsequence, k): queue.append(next_subsequence) # Prune invalid branches early
5. Edge Case: Empty String Result
Don't forget that the result could be an empty string if no valid subsequence exists.
Pitfall Example:
# Assuming there's always a valid answer return result[0] # Could cause IndexError if result is empty
Correct Handling:
# Initialize with empty string and return as-is if no valid subsequence found result = "" # ... BFS logic return result # Correctly returns "" if no valid subsequence exists
Which of the following is a min heap?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Backtracking Template Prereq DFS with States problems dfs_with_states Combinatorial search problems Combinatorial search problems involve finding groupings and assignments of objects that satisfy certain conditions Finding all permutations combinations subsets and solving Sudoku are classic combinatorial problems The time complexity of combinatorial problems often grows rapidly with the size of
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!