Bird
Raised Fist0
DSA Cprogramming

Palindrome Partitioning Using Backtracking in DSA C

Choose your learning style10 modes available
Mental Model
We try to split a string into parts where each part reads the same forwards and backwards. We explore all ways to do this by trying each possible cut and going back if it doesn't work.
Analogy: Imagine cutting a ribbon into pieces where each piece is a perfect mirror image of itself. You try cutting at every spot, and if a piece isn't a mirror, you undo the cut and try a different spot.
s = "a b a c"
Indices: 0 1 2 3
Partitions: [a] [b] [a] [c]
Pointers: start ↑ end ->
We try cuts between letters to form palindrome pieces.
Dry Run Walkthrough
Input: string: "aab"
Goal: Find all ways to split "aab" into palindrome parts
Step 1: Check substring from index 0 to 0: "a" is palindrome, choose it
["a"] + remaining "ab" to partition
Why: Start with smallest palindrome piece to explore partitions
Step 2: Check substring from index 1 to 1: "a" is palindrome, choose it
["a", "a"] + remaining "b" to partition
Why: Continue partitioning remaining string with palindrome pieces
Step 3: Check substring from index 2 to 2: "b" is palindrome, choose it
["a", "a", "b"] complete partition
Why: Reached end with all palindrome pieces, record this partition
Step 4: Backtrack to index 1, try substring 1 to 2: "ab" not palindrome, skip
["a"] + remaining "ab" to partition
Why: Discard invalid palindrome piece and try other options
Step 5: Backtrack to index 0, try substring 0 to 1: "aa" is palindrome, choose it
["aa"] + remaining "b" to partition
Why: Try longer palindrome piece from start
Step 6: Check substring from index 2 to 2: "b" is palindrome, choose it
["aa", "b"] complete partition
Why: Reached end with all palindrome pieces, record this partition
Result:
["a", "a", "b"]
["aa", "b"]
Annotated Code
DSA C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

// Check if substring s[left..right] is palindrome
bool isPalindrome(char *s, int left, int right) {
    while (left < right) {
        if (s[left] != s[right]) return false;
        left++;
        right--;
    }
    return true;
}

// Structure to hold one partition
typedef struct {
    char **parts;
    int size;
    int capacity;
} Partition;

// Initialize Partition
void initPartition(Partition *p) {
    p->capacity = 10;
    p->size = 0;
    p->parts = malloc(sizeof(char*) * p->capacity);
}

// Add part to Partition
void addPart(Partition *p, char *part) {
    if (p->size == p->capacity) {
        p->capacity *= 2;
        p->parts = realloc(p->parts, sizeof(char*) * p->capacity);
    }
    p->parts[p->size++] = strdup(part);
}

// Remove last part from Partition
void removeLastPart(Partition *p) {
    if (p->size > 0) {
        free(p->parts[p->size - 1]);
        p->size--;
    }
}

// Print one partition
void printPartition(Partition *p) {
    for (int i = 0; i < p->size; i++) {
        printf("%s", p->parts[i]);
        if (i != p->size - 1) printf(", ");
    }
    printf("\n");
}

// Backtracking function to find palindrome partitions
void backtrack(char *s, int start, Partition *current) {
    int n = strlen(s);
    if (start == n) {
        printPartition(current); // print one valid partition
        return;
    }
    for (int end = start; end < n; end++) {
        if (isPalindrome(s, start, end)) {
            int len = end - start + 1;
            char *substr = malloc(len + 1);
            strncpy(substr, s + start, len);
            substr[len] = '\0';
            addPart(current, substr); // add palindrome part
            free(substr);
            backtrack(s, end + 1, current); // recurse for remaining string
            removeLastPart(current); // backtrack
        }
    }
}

int main() {
    char s[] = "aab";
    Partition current;
    initPartition(&current);
    backtrack(s, 0, &current);
    // Free allocated memory
    for (int i = 0; i < current.size; i++) {
        free(current.parts[i]);
    }
    free(current.parts);
    return 0;
}
if (isPalindrome(s, start, end)) {
Check if current substring is palindrome to decide if we can partition here
addPart(current, substr); // add palindrome part
Add the palindrome substring to current partition list
backtrack(s, end + 1, current); // recurse for remaining string
Recurse to partition the rest of the string after current palindrome
removeLastPart(current); // backtrack
Remove last added part to try other partitions (undo choice)
OutputSuccess
a, a, b aa, b
Complexity Analysis
Time: O(n * 2^n) because each character can be cut or not, generating exponential partitions, and palindrome checks take O(n) each
Space: O(n) for recursion stack and current partition storage
vs Alternative: Compared to brute force without palindrome checks, this prunes invalid partitions early but still exponential in worst case
Edge Cases
empty string
prints nothing as there are no partitions
DSA C
if (start == n) { printPartition(current); return; }
string with all same characters, e.g. "aaa"
finds all partitions like ["a", "a", "a"], ["aa", "a"], ["a", "aa"], ["aaa"]
DSA C
if (isPalindrome(s, start, end)) { ... }
string with no palindrome longer than 1, e.g. "abc"
only partitions with single characters are found
DSA C
if (isPalindrome(s, start, end)) { ... }
When to Use This Pattern
When asked to find all ways to split a string into palindrome parts, use backtracking to explore all cuts and check palindrome validity to prune invalid paths.
Common Mistakes
Mistake: Not backtracking properly by forgetting to remove last added part after recursion
Fix: Always remove last part after recursive call to explore other partitions
Mistake: Checking palindrome inefficiently multiple times for same substrings
Fix: Use a helper function to check palindrome once per substring or memoize results
Summary
It finds all ways to split a string into palindrome substrings using backtracking.
Use it when you need to list all palindrome partitions of a string.
The key insight is to try every cut, check palindrome, and backtrack to explore all options.

Practice

(1/5)
1. What is the main goal of palindrome partitioning using backtracking on a string s?
easy
A. To find the longest palindrome substring in s
B. To split s into all possible substrings where each substring is a palindrome
C. To count the number of palindromes in s
D. To reverse the string s using recursion

Solution

  1. Step 1: Understand palindrome partitioning

    Palindrome partitioning means dividing the string into parts where each part reads the same forwards and backwards.
  2. Step 2: Role of backtracking

    Backtracking helps explore all possible ways to split the string into palindrome substrings.
  3. Final Answer:

    To split s into all possible substrings where each substring is a palindrome -> Option B
  4. Quick Check:

    Palindrome partitioning = all palindrome substrings [OK]
Hint: Backtracking explores all palindrome splits [OK]
Common Mistakes:
  • Confusing palindrome partitioning with longest palindrome substring
  • Thinking it only counts palindromes instead of listing partitions
  • Assuming it reverses the string
2. Which of the following is the correct base case condition in a backtracking function for palindrome partitioning of string s starting at index start?
easy
A. if (start == s.length()) add current partition to result and return;
B. if (start > s.length()) return false;
C. if (start == s.length()) return true;
D. if (start < 0) return;

Solution

  1. Step 1: Identify when to stop recursion

    When start reaches the length of the string, it means the entire string is partitioned.
  2. Step 2: What to do at base case

    At this point, add the current list of palindrome substrings to the result list and return to explore other partitions.
  3. Final Answer:

    if (start == s.length()) add current partition to result and return; -> Option A
  4. Quick Check:

    Base case adds partition when start reaches string end [OK]
Hint: Add partition when start equals string length [OK]
Common Mistakes:
  • Returning true/false instead of adding partition
  • Checking start > length instead of == length
  • Using invalid index checks like start < 0
3. What will be the output partitions for the string "aab" using palindrome partitioning with backtracking?
medium
A. [["aab"]]
B. [["aa", "ab"]]
C. [["a", "ab"]]
D. [["a", "a", "b"], ["aa", "b"]]

Solution

  1. Step 1: Check all palindrome substrings

    "a" is palindrome, "aa" is palindrome, "b" is palindrome, but "ab" and "aab" are not.
  2. Step 2: List all palindrome partitions

    Partitions are ["a", "a", "b"] and ["aa", "b"].
  3. Final Answer:

    [["a", "a", "b"], ["aa", "b"]] -> Option D
  4. Quick Check:

    Valid palindrome partitions = [["a", "a", "b"], ["aa", "b"]] [OK]
Hint: Check substrings palindrome before partitioning [OK]
Common Mistakes:
  • Including non-palindrome substrings like "ab"
  • Missing the partition ["aa", "b"]
  • Returning the whole string as one partition
4. Given the following code snippet for palindrome partitioning, what is the error?
void backtrack(char* s, int start, char** current, int currentSize, char*** result, int* returnSize) {
  if (start == strlen(s)) {
    result[*returnSize] = current;
    (*returnSize)++;
    return;
  }
  for (int end = start; end < strlen(s); end++) {
    if (isPalindrome(s, start, end)) {
      current[currentSize] = substring(s, start, end);
      backtrack(s, end + 1, current, currentSize + 1, result, returnSize);
    }
  }
}
medium
A. Assigning current directly to result causes shared reference issues
B. The base case condition is incorrect
C. The loop should run from end = 0 to end < strlen(s)
D. Missing return statement after recursive call

Solution

  1. Step 1: Analyze base case assignment

    Assigning current pointer directly to result[*returnSize] stores the same reference for all partitions.
  2. Step 2: Understand backtracking data handling

    We must copy the current partition array to avoid overwriting when backtracking continues.
  3. Final Answer:

    Assigning current directly to result causes shared reference issues -> Option A
  4. Quick Check:

    Copy partitions to result to avoid shared references [OK]
Hint: Copy current partition before adding to result [OK]
Common Mistakes:
  • Not copying current partition array
  • Incorrect loop range for end index
  • Ignoring base case correctness
5. How can you optimize palindrome checking in backtracking palindrome partitioning for string s of length n?
hard
A. Skip palindrome checking and add all substrings to partitions
B. Check palindrome by reversing substring every time during recursion
C. Precompute a 2D boolean table dp where dp[i][j] is true if substring s[i..j] is palindrome
D. Use a hash map to store all substrings and their palindrome status

Solution

  1. Step 1: Understand palindrome checking cost

    Checking palindrome by reversing substring each time costs O(n) per check, leading to O(n^3) overall.
  2. Step 2: Use dynamic programming optimization

    Precompute a 2D table dp where dp[i][j] stores palindrome status for substring s[i..j]. This reduces palindrome checks to O(1).
  3. Step 3: Benefit in backtracking

    Using dp speeds up backtracking by avoiding repeated palindrome checks.
  4. Final Answer:

    Precompute a 2D boolean table dp where dp[i][j] is true if substring s[i..j] is palindrome -> Option C
  5. Quick Check:

    DP table for palindrome checking optimizes backtracking [OK]
Hint: Use DP table to check palindrome in O(1) time [OK]
Common Mistakes:
  • Reversing substring every time causing slow performance
  • Using hash map adds unnecessary overhead
  • Skipping palindrome checks leads to wrong partitions
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard