From a96e98cf960ee8f25ed59c81b353e197c605727f Mon Sep 17 00:00:00 2001 From: Manasvi Reddy Date: Thu, 23 Jul 2026 01:07:14 -0400 Subject: [PATCH] Done Backtracking-2 --- Problem1.py | 29 +++++++++++++++++++++++++++++ Problem2.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 Problem1.py create mode 100644 Problem2.py diff --git a/Problem1.py b/Problem1.py new file mode 100644 index 00000000..c37f6651 --- /dev/null +++ b/Problem1.py @@ -0,0 +1,29 @@ +## Problem1: Subsets (https://leetcode.com/problems/subsets/) +# Time Complexity: O(n x 2^n), There are 2^n subsets total because each number has 2 choices, include or skip. For each subset we spend up to O(n) time to copy the path into the result list. +# Space Complexity: O(n),The path list and the recursion call stack both grow up to size n at the deepest point.This does not count the space used by the result list itself, since that is the output. +# Approach: +# We build subsets by deciding for each number whether to skip it or include it. +# We first explore skipping the number, then come back and explore including it. +# When we reach the end of nums, whatever is in path right now is one complete subset, so we save a copy of it. + +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + self.result = [] # this will hold all the subsets we find + path = [] # this holds the subset we are currently building + + def helper(i: int) -> None: + if i == len(nums): + # we have made a decision for every number, so path is a complete subset now + self.result.append(path.copy()) # save a copy, not path itself, since path keeps changing + return + + # skip choice, we do not add nums[i], just move to the next index + helper(i + 1) + + # include choice, we add nums[i] to path first + path.append(nums[i]) # add nums[i] to the current subset + helper(i + 1) # explore all subsets that include nums[i] + path.pop() # remove nums[i] so path goes back to how it was before, ready for other choices + + helper(0) # start deciding from index 0, path is empty at this point + return self.result \ No newline at end of file diff --git a/Problem2.py b/Problem2.py new file mode 100644 index 00000000..64fcac48 --- /dev/null +++ b/Problem2.py @@ -0,0 +1,38 @@ +# Problem2: Palindrome Partitioning(https://leetcode.com/problems/palindrome-partitioning/) +#Time Complexity: O(2^n * n), For a string of length n, there are roughly 2^n ways to cut it into pieces, since each position can either be a cut point or not. For every one of those partitions, we spend O(n) work doing substring slicing and palindrome checking. +#Space Complexity: O(n^2), The recursion goes n levels deep in the worst case, one level per character.At each level we create new substrings using slicing and slicing costs O(n) each time.Across all n levels this substring creation adds up to O(n^2). This does not count the space used by self.result since that is the output itself. + +#Approach: +#We break the string from the front and check each prefix to see if it is a palindrome. +#If it is, we add it to our current path and keep exploring the rest of the string. +#Once the whole string is used up, we save that valid split and backtrack to try other options. + + +class Solution: + def partition(self, s: str) -> List[List[str]]: + self.result = [] # this will hold every valid palindrome partition we find + self.helper(s, []) # start recursion with full string and an empty path + return self.result # return all collected partitions + + def helper(self, s, path): + if len(s) == 0: # if nothing is left to process + self.result.append(path.copy()) # save a copy of path, not the same list, so future changes do not affect it + return + + for i in range(len(s)): # try every possible length for the next piece + sub_string = s[:i+1] # take characters from start up to index i, i+1 because slicing excludes the last index + if self.isPalindrome(sub_string): # only continue if this piece is a valid palindrome + path.append(sub_string) # action, commit to using this piece + + self.helper(s[i+1:], path) # recurse, pass in everything after this piece as the new remaining string + + path.pop() # backtrack, undo our choice so the next loop iteration starts clean + + def isPalindrome(self, s): + left, right = 0, len(s)-1 # two pointers, one at start, one at end + while left < right: # move inward until pointers meet or cross + if s[left] != s[right]: # compare actual characters, not indices + return False # mismatch found, not a palindrome + left += 1 + right -= 1 + return True \ No newline at end of file