A clear explanation of counting how many repeated copies of one string can be obtained as a subsequence of another repeated string.
Problem Restatement
We are given two strings and two repetition counts:
s1, n1
s2, n2The notation [s, n] means string s repeated n times.
For example:
["abc", 3] = "abcabcabc"Define:
str1 = [s1, n1]
str2 = [s2, n2]We need to find the maximum integer m such that:
[str2, m]can be obtained from str1 by deleting some characters.
In other words, we need to count how many full copies of str2 can be formed as a subsequence of str1. The official statement defines [s, n] as repeated concatenation and asks for the maximum m such that [str2, m] can be obtained from str1.
Input and Output
| Item | Meaning |
|---|---|
| Input | s1, n1, s2, n2 |
str1 | s1 repeated n1 times |
str2 | s2 repeated n2 times |
| Output | Maximum number of full str2 copies obtainable from str1 |
| Matching rule | We may delete characters from str1, so this is subsequence matching |
Function shape:
def getMaxRepetitions(s1: str, n1: int, s2: str, n2: int) -> int:
...Examples
Example 1:
s1 = "acb"
n1 = 4
s2 = "ab"
n2 = 2Then:
str1 = "acbacbacbacb"
str2 = "abab"From str1, we can form two full copies of "abab" as a subsequence.
Answer:
2Example 2:
s1 = "abc"
n1 = 1
s2 = "abc"
n2 = 1Then:
str1 = "abc"
str2 = "abc"We can form one copy.
Answer:
1Example 3:
s1 = "abc"
n1 = 4
s2 = "ab"
n2 = 2Here:
str1 = "abcabcabcabc"
str2 = "abab"Each two copies of s1 can form one copy of "abab".
So the answer is:
2First Thought: Build the Full Strings
A direct solution is to build:
full1 = s1 * n1
full2 = s2 * n2Then repeatedly check how many copies of full2 can be matched as a subsequence of full1.
This is easy to understand, but it fails because n1 and n2 can be as large as 10^6, while each string can have length up to 100.
So the full strings may have length up to:
100 * 1_000_000 = 100_000_000Building them is wasteful.
Problem With Plain Simulation
We can simulate matching s2 while scanning copies of s1.
For each copy of s1, scan its characters and advance an index in s2.
Whenever we finish one s2, increment a counter.
This avoids building str1, but it can still scan:
len(s1) * n1characters.
Since len(s1) <= 100 and n1 <= 10^6, this may be acceptable in Python for many cases. But the cleaner solution observes that there are only len(s2) possible positions inside s2.
We can precompute what one copy of s1 does to each possible starting position in s2.
Key Insight
When scanning one full copy of s1, the only state we need is:
index in s2 we are currently trying to matchSuppose this index is j.
After scanning one copy of s1, two things happen:
- We may complete some number of full
s2matches. - We end at some new index in
s2.
So for every possible starting index j, precompute:
record[j] = (matched_count, next_index)where:
| Field | Meaning |
|---|---|
matched_count | Number of full s2 copies matched while scanning one s1 |
next_index | Position in s2 after that scan |
Then we apply this transition n1 times.
At the end, we have the number of full s2 copies obtainable from [s1, n1].
Since str2 = [s2, n2], the answer is:
full_s2_matches // n2Algorithm
First, handle impossible characters.
If s2 contains a character that never appears in s1, then no copy of s2 can be formed.
Return 0.
Then build records.
For every starting index start from 0 to len(s2) - 1:
- Set
j = start. - Set
count = 0. - Scan each character
chins1. - If
ch == s2[j], advancej. - If
j == len(s2), resetj = 0and incrementcount. - Store
(count, j).
Then simulate n1 copies of s1:
matches = 0
j = 0
for _ in range(n1):
count, j = record[j]
matches += countReturn:
matches // n2Walkthrough
Use:
s1 = "acb"
n1 = 4
s2 = "ab"
n2 = 2Possible starting indices in s2 are:
0 # waiting for 'a'
1 # waiting for 'b'Start at index 0.
Scan "acb":
a matches s2[0] = a, move to index 1
c does not match s2[1] = b
b matches s2[1] = b, complete one s2So:
record[0] = (1, 0)Start at index 1.
Scan "acb":
a does not match s2[1] = b
c does not match s2[1] = b
b matches s2[1] = b, complete one s2So:
record[1] = (1, 0)Now apply four copies of s1.
| Copy | Start index | Full s2 matched | Next index | Total full s2 |
|---|---|---|---|---|
| 1 | 0 | 1 | 0 | 1 |
| 2 | 0 | 1 | 0 | 2 |
| 3 | 0 | 1 | 0 | 3 |
| 4 | 0 | 1 | 0 | 4 |
We can form 4 copies of s2 = "ab".
But str2 = [s2, 2] = "abab".
So the final answer is:
4 // 2 = 2Correctness
For a fixed starting index in s2, scanning one copy of s1 always produces the same result: a fixed number of completed s2 copies and a fixed next index in s2.
The precomputed record table stores exactly this transition for every possible starting index.
The full string [s1, n1] is made of n1 identical copies of s1. Applying the corresponding transition once per copy therefore simulates the same subsequence-matching process as scanning the full repeated string directly.
The variable matches counts how many complete copies of s2 can be obtained from [s1, n1].
Since one copy of str2 consists of n2 copies of s2, the number of complete str2 copies is:
matches // n2Therefore, the algorithm returns the required maximum repetition count.
Complexity
Let:
a = len(s1)
b = len(s2)| Metric | Value | Why |
|---|---|---|
| Time | O(a * b + n1) | Precompute one transition for each s2 index, then apply n1 transitions |
| Space | O(b) | Store one record per starting index in s2 |
This avoids building either repeated string.
Implementation
class Solution:
def getMaxRepetitions(self, s1: str, n1: int, s2: str, n2: int) -> int:
if n1 == 0:
return 0
if not set(s2).issubset(set(s1)):
return 0
m = len(s2)
records = []
for start in range(m):
count = 0
j = start
for ch in s1:
if ch == s2[j]:
j += 1
if j == m:
count += 1
j = 0
records.append((count, j))
matches = 0
j = 0
for _ in range(n1):
count, j = records[j]
matches += count
return matches // n2Code Explanation
First, we handle the case where no copies of s1 exist:
if n1 == 0:
return 0Then we check whether every character in s2 appears in s1:
if not set(s2).issubset(set(s1)):
return 0If a needed character never appears in s1, no complete s2 can be formed.
The table:
records = []stores what happens after scanning one s1 from each possible position in s2.
For each starting position:
for start in range(m):we scan s1 and advance j whenever a character matches:
if ch == s2[j]:
j += 1When j == m, one full s2 has been matched:
count += 1
j = 0After precomputation, we apply the transition once per copy of s1:
for _ in range(n1):
count, j = records[j]
matches += countFinally, divide by n2:
return matches // n2because the target repeated string contains n2 copies of s2.
Faster Version With Cycle Skipping
The previous version is already efficient enough for the constraints.
We can also detect repeated states during the n1 simulation.
A state is the current index j in s2.
If the same j appears again after some number of s1 copies, then the process has entered a cycle.
class Solution:
def getMaxRepetitions(self, s1: str, n1: int, s2: str, n2: int) -> int:
if n1 == 0:
return 0
if not set(s2).issubset(set(s1)):
return 0
index = 0
s1_count = 0
s2_count = 0
seen = {}
while s1_count < n1:
s1_count += 1
for ch in s1:
if ch == s2[index]:
index += 1
if index == len(s2):
s2_count += 1
index = 0
if index in seen:
prev_s1_count, prev_s2_count = seen[index]
cycle_s1 = s1_count - prev_s1_count
cycle_s2 = s2_count - prev_s2_count
remaining_s1 = n1 - s1_count
cycles = remaining_s1 // cycle_s1
s1_count += cycles * cycle_s1
s2_count += cycles * cycle_s2
else:
seen[index] = (s1_count, s2_count)
return s2_count // n2The precomputed transition version is simpler and has predictable O(len(s1) * len(s2) + n1) complexity. The cycle-skipping version can be faster when n1 is very large.
Testing
def run_tests():
s = Solution()
assert s.getMaxRepetitions("acb", 4, "ab", 2) == 2
assert s.getMaxRepetitions("abc", 1, "abc", 1) == 1
assert s.getMaxRepetitions("abc", 4, "ab", 2) == 2
assert s.getMaxRepetitions("aaa", 3, "aa", 1) == 4
assert s.getMaxRepetitions("abc", 10, "d", 1) == 0
assert s.getMaxRepetitions("baba", 11, "baab", 1) == 7
assert s.getMaxRepetitions("a", 0, "a", 1) == 0
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
"acb", 4, "ab", 2 | Standard example |
"abc", 1, "abc", 1 | Exact one-copy match |
"abc", 4, "ab", 2 | Repeated target grouping |
"aaa", 3, "aa", 1 | Dense repeated characters |
"abc", 10, "d", 1 | Impossible character |
"baba", 11, "baab", 1 | Non-trivial subsequence alignment |
n1 = 0 | Empty source repetition |