Back to Strings
Q13Question 13 of 61

Palindrome Partitioning — backtrack with palindrome check at each cut

Key Concepts
  • String manipulation: reverse, palindrome, anagram check
  • Character frequency counting with HashMap
  • Substring search, pattern matching
  • String is immutable in Java — use StringBuilder for modification
  • Carry a start index and a path of substrings.
  • When start equals the length, record a copy of the path.
  • Otherwise loop end from start to the end; if substring start.

Definition

Try every prefix cut, keep only palindromic prefixes, recurse on the remainder until the whole string is consumed.

Backtrack over cut positions: at a start index, try every end index, and if the substring start..end is a palindrome, add it to the path and recurse from end+1. When start reaches the string length, the path is a complete valid partition. The palindrome test at each cut prunes branches that can never lead to a valid partition.

National Bank of India must split a reference string into symmetric blocks for a checksum scheme. At each point you try cutting off a leading block; you keep it only if it reads the same forwards and backwards, then continue splitting the rest. Every way of splitting into all-symmetric blocks is a valid answer.

Understanding the Concepts

What is the problem?

Palindrome Partitioning — backtrack with palindrome check at each cut. Given a string, partition it so that every substring of the partition is a palindrome, and return all possible palindrome partitionings.

How does the approach work?

Carry a start index and a path of substrings. When start equals the length, record a copy of the path. Otherwise loop end from start to the end; if substring start..end is a palindrome, add it, recurse from end+1, and remove it. For example: if (isPalindrome(s,start,end)) { path.add(s.substring(start,end+1)); backtrack(end+1); path.remove(last); }.

The palindrome check uses two pointers moving inward. For better performance you can precompute a boolean DP table isPal[i][j] so each check is O(1), making the overall cost dominated by the number of partitions.

The worst-case time is O(n · 2^n) because a string of all identical characters has 2^(n-1) partitions, each of length up to n. A senior nuance is that the DP precomputation turns repeated palindrome checks from O(n) each into O(1), which matters on adversarial inputs.

What is the banking use case?

Problem 1 — At National Bank of India, a legacy reference code must be split into symmetric segments for a validation scheme, and the tool needs every valid segmentation. This is exactly how palindrome partitioning works.
Problem 2 — A segment is only acceptable if it is a palindrome, and the segments together must cover the whole code. This is exactly why each prefix is palindrome-checked before recursing on the remainder.

The mini-project builds a ReferenceSegmenter, a BankLogFormatter, and a runnable demo.

How do you implement this in Java?

JAVA
// ─── File: src/main/java/com/bank/refval/ReferenceSegmenter.java ───
package com.bank.refval;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

public class ReferenceSegmenter {

    private static final Logger log = Logger.getLogger(ReferenceSegmenter.class.getName());

    public List<List<String>> partition(String s) {
        List<List<String>> result = new ArrayList<>();
        backtrack(s, 0, new ArrayList<>(), result);
        log.info(() -> "Found " + result.size() + " palindrome segmentations of '" + s + "'");
        return result;
    }

    private void backtrack(String s, int start, List<String> path, List<List<String>> result) {
        if (start == s.length()) { result.add(new ArrayList<>(path)); return; }
        for (int end = start; end < s.length(); end++) {
            if (isPalindrome(s, start, end)) {         // keep only palindromic prefix
                path.add(s.substring(start, end + 1));
                backtrack(s, end + 1, path, result);
                path.remove(path.size() - 1);
            }
        }
    }

    private boolean isPalindrome(String s, int lo, int hi) {
        while (lo < hi) {
            if (s.charAt(lo++) != s.charAt(hi--)) return false;
        }
        return true;
    }
}
JAVA
// ─── File: src/main/java/com/bank/refval/BankLogFormatter.java ───
package com.bank.refval;

import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import java.time.Instant;

public class BankLogFormatter extends Formatter {
    @Override
    public String format(LogRecord record) {
        return String.format("%s [%s] %s%n",
                Instant.ofEpochMilli(record.getMillis()),
                record.getLevel(),
                record.getMessage());
    }
}
JAVA
// ─── File: src/main/java/com/bank/refval/PalindromePartitionDemo.java ───
package com.bank.refval;

import java.util.logging.ConsoleHandler;
import java.util.logging.Logger;

public class PalindromePartitionDemo {

    private static final Logger log = Logger.getLogger(PalindromePartitionDemo.class.getName());

    static {
        Logger root = Logger.getLogger("");
        for (var h : root.getHandlers()) root.removeHandler(h);
        ConsoleHandler handler = new ConsoleHandler();
        handler.setFormatter(new BankLogFormatter());
        root.addHandler(handler);
    }

    public static void main(String[] args) {
        log.info("National Bank of India — Reference Code Segmenter (Palindrome Partition)");
        ReferenceSegmenter seg = new ReferenceSegmenter();
        seg.partition("aab").forEach(p -> log.info("Segmentation: " + p));
    }
}

What is the project structure?

DIAGRAM
reference-validation-service/
├── pom.xml
├── src/main/java/com/bank/refval/
│   ├── PalindromePartitionDemo.java (main runnable demo)
│   ├── ReferenceSegmenter.java      (backtracking + palindrome check)
│   └── BankLogFormatter.java
└── src/test/java/com/bank/refval/
    └── ReferenceSegmenterTest.java

How do you build and run?

BASH
# Compile
javac -d target/classes src/main/java/com/bank/refval/*.java

# Run
java -cp target/classes com.bank.refval.PalindromePartitionDemo

What is the step-by-step walkthrough?

The input is the string a, a, b.

Step 1: At start zero, the prefix "a" is a palindrome, so recurse on "ab".
Step 2: In "ab", "a" is a palindrome, then "b" is a palindrome, giving the partition [a, a, b].
Step 3: Back at start zero, the prefix "aa" is a palindrome, so recurse on "b".
Step 4: "b" is a palindrome, giving the partition [aa, b].
Step 5: The two valid palindrome segmentations are produced.
TEXT
2026-10-27T00:25:00Z [INFO] National Bank of India — Reference Code Segmenter (Palindrome Partition)
2026-10-27T00:25:00Z [INFO] Found 2 palindrome segmentations of 'aab'
2026-10-27T00:25:00Z [INFO] Segmentation: [a, a, b]
2026-10-27T00:25:00Z [INFO] Segmentation: [aa, b]

What is the high-level design?

DIAGRAM
  ═══ HLD — Reference Code Segmenter ═══

  ┌────────────┐ code ┌──────────────┐ ┌──────────────────┐
  │ Ref Store  │─────→│ Validate Svc │→│ palindrome DFS   │
  └────────────┘      │ (Spring ×N)  │ └──────────────────┘
                      └──────┬───────┘
                             │ segmentations → checksum module
                             ▼
                        Checksum Engine

What is the low-level design?

DIAGRAM
  ═══ LLD — Palindrome Partitioning ═══

  ┌─ Technique ──────────────────────────────────────────────┐
  │  try every prefix cut; keep palindromic ones; recurse     │
  │  precompute isPal[i][j] → O(1) checks (optimization)      │
  └──────────────────────────────────────────────────────────┘

  ┌───────────────────────────────────────┐
  │   ReferenceSegmenter                  │
  │  + partition(s): List<List<String>>   │ O(n·2^n)
  │  - backtrack(s,start,path,result)     │
  │  - isPalindrome(s,lo,hi): boolean     │
  └───────────────────────────────────────┘

What is the time and space complexity?

Analyze the solution based on the algorithm used. Consider both the time to process input and the space required for any data structures.

What are common mistakes?

Common mistakes include off-by-one errors, not handling edge cases like empty input, and not considering the constraints of the problem.

What are the edge cases?

Edge cases include empty input, single element input, all identical elements, and boundary values.

What should a senior engineer know?

A senior engineer should understand the time-space tradeoffs, be able to explain the algorithm clearly, and know when this technique applies versus alternatives.

Visual Overview

DIAGRAM
  "aab"
    "a" pal → "ab" → "a" + "b" → [a,a,b]
    "aa" pal → "b" → [aa,b]
    "aab" not pal → pruned
  partitions = 2

Key Takeaway

Palindrome Partitioning asks for every way to split a string so that each piece is a palindrome. I solve it with backtracking over cut positions: from a start index I try every possible end index, and if the substring from start to end is a palindrome I add it to the current path and recurse from the position just after it. When the start index reaches the end of the string, the path holds a complete valid partition and I record a copy.

The palindrome check at each cut is what prunes the search, because a non-palindromic prefix can never belong to a valid partition and that branch is abandoned immediately. For adversarial inputs like a run of identical characters I precompute a boolean table of which substrings are palindromes, turning each check into constant time; the worst-case number of partitions is still exponential.

In my project at National Bank of India, a legacy reference code had to be split into symmetric segments for a validation scheme, and the tool enumerated every valid segmentation. The backtracking produced them in order, and the precomputed palindrome table kept it fast on the pathological all-same-character references.

Remember one sentence: understand the core algorithm and apply it to banking domain problems.

---

Share
Use ← → arrow keys to navigate