Strings · String Fundamentals

String Repetition Search

Figuring out the minimum number of repetitions of one string needed before another could possibly appear as a substring, from length alone - then checking only that small number of candidate repetition counts instead of an unbounded search.

  • 5 min read
  • 8 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: look inside s + s
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

How many copies of "wxyz" does it take to contain "yzwxyzwx"? Is "erwat" a rotation of "water"? Is "xyzxyzxyz" just "xyz" repeated? These are questions about periodic strings. The trick in each is knowing how much repetition can possibly matter, so the search stays bounded and short. This skill covers the copy bound, the doubling trick, and the period that the KMP failure function gives you for free.

We'll go from simple to more technical:

  1. The pattern: repeat until long enough, then at most one more copy.
  2. A second flavor: look inside s + s for rotations and repeated blocks.
  3. Where periodicity shows up in production (rotations, circular IDs, ring buffers).
  4. What breaks it: no bound, one copy too few, missing length checks, untrimmed doubling.

The approach

Java

int repeatedStringMatch(String a, String b) {
    int q = (b.length() + a.length() - 1) / a.length();   // ceil(|b| / |a|): the fewest copies long enough
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < q; i++) sb.append(a);
    if (sb.indexOf(b) >= 0) return q;
    if (sb.append(a).indexOf(b) >= 0) return q + 1;       // b may straddle into one more copy
    return -1;
}

Python

def repeated_string_match(a: str, b: str) -> int:
    q = -(-len(b) // len(a))                         # ceil(|b| / |a|): the fewest copies long enough
    if b in a * q:
        return q
    if b in a * (q + 1):                             # b may straddle into one more copy
        return q + 1
    return -1

Fewer than q copies are too short to hold b. More than q + 1 never helps: any occurrence of b can be shifted back by whole copies of a until it starts inside the first copy, at an offset below |a|, so it ends before |a| + |b|, which is at most (q + 1) copies. If q + 1 copies don't contain b, no number of copies does. "abc" and "cabcabca" show why the extra copy matters: 3 copies miss it, 4 contain it.

A second flavor: look inside s + s

Doubling a string lays out every rotation of it side by side:

Java

boolean isRotation(String a, String b) {
    return a.length() == b.length() && (a + a).contains(b);
}

boolean repeatedSubstringPattern(String s) {             // two or more copies of one block?
    String doubled = s + s;
    return doubled.substring(1, doubled.length() - 1).contains(s);
}

Python

def is_rotation(a: str, b: str) -> bool:
    return len(a) == len(b) and b in a + a


def repeated_substring_pattern(s: str) -> bool:      # two or more copies of one block?
    return s in (s + s)[1:-1]

Every rotation of a appears in a + a - but so does every shorter substring, so the length check is essential: without it, "a" counts as a rotation of "ab". For repeated blocks, s always occurs in s + s at positions 0 and n; removing the first and last characters rules those out, and s still occurs elsewhere exactly when shifting it by some period gives s again - that is, when s is made of a repeated block.

Where you'll see it

Detecting a repeating unit in logs or sensor readings, comparing circular identifiers (the same cycle starting at a different point), and finding tandem repeats in DNA all use periodicity. Circular buffers and ring-shaped data are routinely "unrolled" by doubling so a wrapping sequence becomes an ordinary substring.

Which tool for which job?

You needUse
Fewest copies of a containing bcopies until the length is ≥ len(b), then try one more
Is b a rotation of a?equal lengths and b in a + a
Is s a repeated block?s in (s + s)[1:-1], or the KMP period test
The smallest periodp = n - lps[n - 1]
A canonical form for rotation-equivalent stringsthe least rotation (Booth's algorithm)
Guaranteed linear search in JavaKMP or Z, not indexOf

Common mistakes

MistakeWhat happensDo this instead
No upper bound on copiesnever ends when b can't occurat most ceil(len(b) / len(a)) + 1 copies
Stopping at q copiesmisses matches that straddle copiestry q + 1
Rotation without a length check"a" counts as a rotation of "ab"compare lengths first
Untrimmed doublingeverything looks repeatedsearch (s + s)[1:-1]
Trusting Java's indexOf to be linearO(n × m) worst caseKMP for adversarial input

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why are ceil(|b| / |a|) + 1 copies of a always enough to decide whether b fits?

Show answer

Any occurrence of b in repeated copies of a can be shifted back by whole copies until it starts inside the first copy, at an offset below |a|. It then ends before |a| + |b| characters, which fit in ceil(|b| / |a|) + 1 copies. If that many copies don't contain b, no number of copies does.

2. Why does a rotation check need a.length() == b.length() as well as (a + a).contains(b)?

Show answer

a + a contains every rotation of a - but also every substring of those rotations. Without the length check, "a" would count as a rotation of "ab".

3. Why does the repeated-block check remove the first and last characters of s + s?

Show answer

s occurs in s + s at positions 0 and n for every string. Trimming one character from each end destroys those two occurrences; s can still be found only at a shift that is a period of s, which happens exactly when s is two or more copies of a block.

Key takeaways

  • b fits in copies of a within ceil(|b| / |a|) or one more copy - never more.
  • Rotations of a all live in a + a; compare lengths too.
  • s is a repeated block exactly when it occurs in (s + s) minus its first and last characters.
  • Bound every "repeat until" loop.
  • indexOf is naive: O(n * m) worst case; KMP makes it linear.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Fewest copiesq, then q + 1
Rotationequal lengths plus b in a + a
Repeated block(s + s)[1:-1], or the KMP period
Canonical rotationBooth's least rotation
Linear search in JavaKMP / Z

Snippets worth knowing by heart

Java

boolean isRotation(String a, String b) { return a.length() == b.length() && (a + a).contains(b); }
int period = n - lps[n - 1];                       // smallest period from the failure function
boolean repeated = period < n && n % period == 0;

Python

def repeated_block(s):
    return s in (s + s)[1:-1]

The traps

  • No bound on copies: the loop never ends when b can't occur. Stop at ceil(len(b) / len(a)) + 1 copies.
  • One copy too few: a match can straddle copies. Try q + 1 copies too.
  • No length check for rotations: "a" counts as a rotation of "ab". Compare lengths first.
  • Untrimmed doubling: every string is found in s + s. Search (s + s)[1:-1].
  • indexOf's worst case: Java's indexOf is O(n x m) on adversarial input. Use KMP there.

Staff-level points, one line each

  • The KMP period test matched the doubling check on 2,000 strings.
  • Booth's least rotation matched brute force on 2,000 strings and grouped water/terwa/erwat apart from retaw/awret.
  • Java indexOf was at least 10x slower than KMP on 400,000 a's with an a...ab pattern.
  • CPython's Two-Way search handled 1,000,000 a's in under 0.5 s.

Say it in an interview

  • "Every rotation of a is inside a + a, as long as the lengths match."
  • "s is a repeated block exactly when it shows up inside (s + s) trimmed at both ends."
  • "The smallest period is n minus the last failure-function value."
  • "For a guaranteed linear search I'd use KMP, not indexOf."

Practice

This skill comes with 8 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice String Repetition Search free

Repeated String Match

MediumProCoding problemJava · Python

How many copies of a, written one after another, does it take before b shows up somewhere inside them? Return that number, or -1 if no number of copies is enough.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice String Repetition Search free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime