Feat: Backtracking Notes

This commit is contained in:
riyabansal98 2019-10-15 14:51:00 +05:30
parent 0bb30e9451
commit d72c7f29aa
3 changed files with 286 additions and 6 deletions

View File

@ -28,7 +28,8 @@ _For example_, we compute factorial n if we know factorial of (n-1). The base ca
Power Power
----- -----
> Given n, k. Find $n^k$ > Given n, k.
Find $n^k$
```python ```python
def pow(n, k): def pow(n, k):
@ -199,9 +200,10 @@ Because we can have negative values in the array as well and this condition will
For eg, For eg,
target = 6 target = 6
1, 2, 3 is good, but 1, 2, 3 is good, but
1, 2, 3, -1, 1 is also good. 1, 2, 3, -1, 1 is also good. <br>
__Time Complexity__: $O(2^n)$ __Time Complexity__: O(2^n)
__Space Complexity__: $O(n)$ <br>
__Space Complexity__: O(n)
Number of Subsets with a given Sum (Repetition Allowed) Number of Subsets with a given Sum (Repetition Allowed)
--------------- ---------------
@ -211,6 +213,8 @@ Number of Subsets with a given Sum (Repetition Allowed)
The subsetSum2 problem can be divided into two subproblems. The subsetSum2 problem can be divided into two subproblems.
- Include the current element in the sum and recur for the rest of the array. Here the value of i is not incremented to incorporate the condition of including multiple occurances of a element. - Include the current element in the sum and recur for the rest of the array. Here the value of i is not incremented to incorporate the condition of including multiple occurances of a element.
- Exclude the current element from the sum and recur (i = i + 1) for the rest of the array. - Exclude the current element from the sum and recur (i = i + 1) for the rest of the array.
![IMG_0040](https://user-images.githubusercontent.com/35702912/66470200-c27db600-eaa6-11e9-8744-ca572d6000e1.jpg) ![IMG_0040](https://user-images.githubusercontent.com/35702912/66470200-c27db600-eaa6-11e9-8744-ca572d6000e1.jpg)
```python ```python
def subsetSum2(A,N,cur_sum, i, target): def subsetSum2(A,N,cur_sum, i, target):
@ -225,8 +229,9 @@ def subsetSum2(A,N,cur_sum, i, target):
no_take = subsetSum2(A,N,cur_sum, i+1, target) no_take = subsetSum2(A,N,cur_sum, i+1, target)
return take + no_take return take + no_take
``` ```
__Time Complexity__ : $O(2 ** (Target/MinElement))$ __Time Complexity__ : _O(2 *(Target/MinElement))_
__Space Complexity__: $O(Target/Min Element)$ <br>
__Space Complexity__: _O(Target/Min Element)_

View File

@ -0,0 +1,164 @@
Backtracking
------------
Backtracking is a methodical way of trying out various sequences of decisions, until you find one that “works”. It is a systematic way to go through all the possible configurations of a search space.
- do
- recurse
- undo
Backtracking is easily implemented with recursion because:
- The run-time stack takes care of keeping track of the choices that got us to a given point.
- Upon failure we can get to the previous choice simply by returning a failure code from the recursive call.
Backtracking can help reduce the space complexity, because we're reusing the same storage.
__Backtracking Algorithm__:
Backtracking is really quite simple--we “explore” each node, as follows:
```python
To “explore” node N:
1. If N is a goal node, return “success”
2. If N is a leaf node, return “failure”
3. For each child C of N,
3.1. Explore C
3.1.1. If C was successful, return “success”
4. Return “failure”
```
Print all Permutations of a String
-------------
> A permutation, also called an “arrangement number” or “order,” is a rearrangement of the elements of an ordered string S into a one-to-one correspondence with S itself. <br>
String: ABC <br>
Permutations: ABC ACB BAC BCA CBA CAB
Total permutations = n!
<img src="https://user-images.githubusercontent.com/35702912/66570095-7e63e180-eb8a-11e9-8e3c-31d8e04f2d67.jpg" width="500"
/>
<img src="https://user-images.githubusercontent.com/35702912/66570104-83c12c00-eb8a-11e9-802d-f0f0ede4a14a.jpg" width="500"
/>
```python
def permute(S, i):
if i == len(S):
print(S)
for j in range(i, len(S)):
S[i], S[j] = S[j], S[i]
permute(S, i+1)
S[i], S[j] = S[j], S[i] # backtrack
```
__Time Complexity:__ _O(n*n!)_ because there are n! permutations and it requires _O(n)_ to print a permutation.
<br>
__Space Complexity:__ _O(n)_
_Note: Output not in Lexicographic Order._
Print all Unique Permutations of a String
--------------------
> String: AAB
> Permutations: AAB ABA BAA
Basically, if we're swapping S[i] with S[j], but S[j] already occured earlier from S[i] .. S[j-1], then swapping will result in repetition.
<img src="https://user-images.githubusercontent.com/35702912/66570690-bc153a00-eb8b-11e9-8a00-9dfb728df5f9.jpg" width="500"
/>
```python
def permute_distinct(S, i):
if i == len(S):
print(S)
for j in range(i, len(S)):
if S[j] in S[i:j]:
continue
S[i], S[j] = S[j], S[i]
permute_distinct(S, i+1)
S[i], S[j] = S[j], S[i] # backtrack
```
__Time Complexity:__ _O(n*n!)_ <br>
__Space Complexity:__ _O(n)_
Print Permutations Lexicographically
---
> Given a string, print all permutations of it in sorted order. <br>
For example, if the input string is “ABC”, then output should be “ABC, ACB, BAC, BCA, CAB, CBA”.
- Right shift the elements before making the recursive call.
- Left shift the elements while backtracking.
```python
def permute(S, i):
if i == len(S):
print(S)
for j in range(i, len(S)):
S[i], S[j] = S[j], S[i]
permute(S, i+1)
S[i], S[j] = S[j], S[i] # backtrack
```
__Time Complexity:__ _O(n* n*n!)_ <br>
__Space Complexity:__ _O(n)_
Kth Permutation Sequence (Optional)
----
> Given a string of length n containing lowercase alphabets only. You have to find the k-th permutation of string lexicographically.
$\dfrac{k}{(n-1)!}$ will give us the index of the first digit. Remove that digit, and continue.
```python
def get_perm(A, k):
perm = []
while A:
# get the index of current digit
div = factorial(len(A)-1)
i, k = divmod(k, div)
perm.append(A[i])
# remove handled number
del A[index]
return perm
```
Sorted Permutation Rank (Optional)
--
> Given S, find the rank of the string amongst its permutations sorted lexicographically.
Assume that no characters are repeated.
```python
Input : 'acb'
Output : 2
The order permutations with letters 'a', 'c', and 'b' :
abc
acb
bac
bca
cab
cba
```
**Hint:**
If the first character is X, all permutations which had the first character less than X would come before this permutation when sorted lexicographically.
Number of permutation with a character C as the first character = number of permutation possible with remaining $N-1$ character = $(N-1)!$
**Approach:**
rank = number of characters less than first character * (N-1)! + rank of permutation of string with the first character removed
```
Lets say out string is “VIEW”.
Character 1 : 'V'
All permutations which start with 'I', 'E' would come before 'VIEW'.
Number of such permutations = 3! * 2 = 12
Lets now remove V and look at the rank of the permutation IEW.
Character 2 : I
All permutations which start with E will come before IEW
Number of such permutation = 2! = 2.
Now, we will limit ourself to the rank of EW.
Character 3:
EW is the first permutation when the 2 permutations are arranged.
So, we see that there are 12 + 2 = 14 permutations that would come before "VIEW".
Hence, rank of permutation = 15.
```

View File

@ -0,0 +1,111 @@
Number of Squareful Arrays
--------------------------
> Given A[N]
> array is squareful if for every pair of adjacent elements, their sum is a perfect square
> Find and return the number of permutations of A that are squareful
>
Example:
A = [2, 2, 2]
output: 1
A = [1, 17, 8]
output: 2
[1, 8, 17], [17, 8, 1]
```python
def check(a, b):
sq = int((a + b) ** 0.5)
return (sq * sq) == (a + b)
if len(A) == 1: # corner case
return int(check(A[0], 0))
count = 0
def permute_distinct(S, i):
global count
if i == len(S):
count += 1
for j in range(i, len(S)):
if S[j] in S[i:j]: # prevent duplicates
continue
if i > 0 and (not check(S[j], S[i-1])): # invalid solution - branch and bound
continue
S[i], S[j] = S[j], S[i]
permute_distinct(S, i+1)
S[i], S[j] = S[j], S[i] # backtrack
permute_distinct(A, 0)
return count
```
Gray Code
---------
> Given a non-negative integer N representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0.
> The gray code is a binary numeral system where two successive values differ in only one bit.
G(n+1) can be constructed as:
0 G(n)
1 R(n)
```
Example G(2) to G(3):
0 00
0 01
0 11
0 10
----
1 10
1 11
1 01
1 00
```
```python
def gray(self, n):
codes = [0, 1] # length 1
for i in range(1, n):
new_codes = [s | (1 << i) for s in reversed(codes)]
codes += new_codes
return codes
```
N Queens
--------
[NQueens - InterviewBit](https://www.interviewbit.com/problems/nqueens/)
Backtracking
- Place one queen per row
- backtrack if failed
Word Break II
-------------
> Given a string A and a dictionary of words B, add spaces in A to construct a sentence where each word is a valid dictionary word.
```
Input 1:
A = "catsanddog",
B = ["cat", "cats", "and", "sand", "dog"]
Output 1:
["cat sand dog", "cats and dog"]
```
```python
def wordBreak(A, B):
B = set(B)
sents = []
def foo(i, start, sent):
word = A[start:i+1]
if i == len(A):
if word in B:
sents.append((sent + ' ' + word).strip())
return
if word in B:
foo(i+1, i+1, sent + ' ' + word)
foo(i+1, start, sent)
foo(0, 0, '')
```