Unique Length 3 Palindromic Subsequences - Leetcode 1930 - Python

NeetCodeIO · Intermediate ·⚡ Algorithms & Data Structures ·7mo ago

Key Takeaways

The video solves the Unique Length 3 Palindromic Subsequences problem on Leetcode using Python, utilizing data structures such as hash sets and hash maps to count unique palindromic subsequences in O(n) time. It demonstrates the use of a hash set and a hash map to store characters on the left and right sides, and a constant time lookup to find matching characters.

Full Transcript

Hey everyone, welcome back and let's write some more neat code today. And today's video actually I've already recorded before today's problem. And I was looking at it and seeing if it was good enough. And I have to say for a video that I made over 3 years ago back when I was still unemployed. This one is actually not too bad. I'm going to re-record it though because I think I could probably explain the exact same thing in about like half the time. Let's see if I turn out to be correct or not. But uh here goes. unique length three palendromeic substrings. So the idea is we're given an input string of lowercase a through z character. So for this input string example we have a a b c a and that's the only parameter we're given a string. And so what we want to do is count how many length three palindromes are there. What's a palenrome? Basically something that is read the same forward and back. So like a a a is a palendrome. ABA AA is a palendrome. ABC is not a palendrome. But we're not looking for substrings this time. We are looking for subsequences. So you might be familiar with subsequences. They come up a lot in dynamic programming problems. The idea is that basically it's going to be non-ontiguous, but the order matters. So we could pick like let's say that, that, and maybe this one. See how they're not contiguous. So we have like a gap here but we would say that this subsequence is a b c. Before you go down that brute force backtracking dynamic programming path recognize that we are only considering the subsequences that are length three. So you tell me what would a brute force solution look like? Do we really have to get recursive? Can't we just do triple nested loops? Like for example I have one pointer which will start here. I'll have another pointer which will start after that and then another pointer that will start after that because having triple nested loops we can still guarantee that the relative order will be the same. So maybe if my J was over here now and then my K starts after that this is still like preserving the order. So yes, we can get every single subsequence in three loops. That's going to be n cubed time. And determining if the like if that's a palrum, that subsequence of length three, that's technically constant time. It's of length three. That is a small length. It's fixed. So it's going to be very easy for us to determine that. In fact, if you give me three characters that look like this, I don't really care what goes in the middle. It does not matter. I only care that the left and right characters are equal. So that is going to be very trivial to check. Now the question becomes, how can we do better than an N cubed solution? Does knowing that the middle character doesn't matter help us at all? Well, it kind of does because consider this. If I was like scanning through here, let's say I call my pointer mid and it tells me what the middle character is. Let's say it's over here some arbitrary position. How do I know how many palendromes I can form with this guy as the middle character? Well, here's what I would do. I'll just go through every character on the left side. Let's say I see an A. Well, now I want to know, do I have any A's on the right side? And it turns out we do. So, we have one here. So, we found a palendrome. So what I'm trying to do at this point is say for every middle character, can I somehow in linear time go through everything on the left and everything on the right and just count the palms with this guy as a middle character because then I can get the solution down from n cubed to n squ and yes that is actually possible. One way to do it would be let's say just throw all of these into some data structure uh like a hash set and then do the same thing over here. throw them into a hash and then what I could do is go through every character on the left side and so we have an a here and then I'd say okay let's do a quick lookup a constant time lookup to see if we also have that same character in the right side and that would allow me to count the palendrome so that's the n squared way the n squ comes from the fact that we're having to take all of these and then take all of these and then throw them into a hash set and we have to do that every single time with this pointer like every time we shift the pointer now if I were to move my middle pointer over here. I have to redo that. Now I have to throw all of these into the left side and then all of these into the right side. Do you kind of see how we are doing some repeated work there? Why do we have to do all of these and all of these every single time that we take our middle pointer and shift it? We don't. We could just focus on the new character that we added. Throw it on the left side. And since we shifted the middle pointer, we take this guy and then remove it from the right side. So that's the intuition behind this solution. That's the intuition of how to get this down to a linear time solution. Now, let me dry run through this and show you a couple minor things that I'm going to change. One of them being that I actually don't want a hash set on the right side. I actually want a hash map because if I'm removing a character from the right side, let's say I want to remove the C. Well, if I have multiple occurrences of the C, I want to be able to somehow track that. On the left side, it doesn't matter. We can use a hashet because we're never removing characters from the left side. We're only adding characters. If I have multiple A's on the left side, it doesn't actually matter. And the main reason for that is in this problem, they actually only want us to track the unique palendrome. So if I were to have like a b a and maybe I can form that another way with a second a on the left side. It doesn't matter. It's technically still the same palendrome. So we don't want to double count it. And the easiest way to do that is to keep track of all the palenromes that we've seen in a data structure. I'm going to use a hash set. It's pretty easy to do that. And in fact we don't actually need to store the entire palrrome itself. Suppose we had aba as the palrrome. This entire information can be encapsulated in two characters. We only need the left character and the middle character. You could call this the middle character and you could call this the left or you could call it the outer character. The reason I say outer character is because since it's a palendrome, we know this character is going to be the same as the one on the right side. Anyway, so in fact, we can just store two characters um in our hashet for each palendrome. Now, quickly, let me go through the dry run. So, this is how it's going to work right now. Now this is our middle pointer. I'm going to have my left side that's going to be a hash set and my right side is going to be a hashmap. Initially my map is going to be I have three occurrences of A. I have one occurrence of B and I have one occurrence of C. We basically initialize this with all of the characters. When my middle pointer reaches here, I want to now take this out of the right side because now this is our right side. So I can decrement the count of a by one. It's going to be two now. But so far there's nothing on our left side yet. So now I'm going to take my middle pointer, move it here. I'm going to take this guy and throw it in the left side. So I have an A on the left side. And now that we've removed this from the right side, we can decrement the count of a by one. It'll be one. I know this is a little bit hard to read. So now to count the palenromes where this is the middle character, we go through every character on our left side and check does this character also exist in the map on the right side. And in fact it does. We have an A here and an A here. So we counted one palrome. Middle pointer is going to be here. Now we have two A's on the left side. Doesn't matter. We don't keep track of them. We remove the B from the right side. So the count of B now is going to be zero. Once again, we go through all the characters on the left. It's an A. Look up. Does A exist on the right side? Yep. So, we can in fact form a palendrome. That's two. And now shift middle over here. B is thrown into the left side. B is rem or C is removed from the right side. So, I realized I had the count wrong here. It should have been a one. Sorry. Um, but now we have zero C's. And once again, I go through everything on the left set. A does it also exist in the map? Yes, it does. So once again, we can form a palendrome here. But we check B. B exists over here, but not on the right side, not in this map. Thus, we can't form a palenrome. And finally, when our middle pointer gets over here, I think it's obvious that there's nothing on the right side anyway. This will be down to zero. So of course, we can't form a palenrome. But we counted three total palrums. That is the solution to this. And we're doing it in linear time, but obviously we do have linear space as well. And just in case you're wondering, if we're throwing every single possible palendrome into the hash set, how could that be efficient? Well, think about how many different palendromes we could have given that we're only dealing with lowercase A through Z characters. And given that we have three different spots, you might think there's 26 cubed palendromes, but not really because we know that the left and right are always going to be the same. So we could have 26 different positions here. And then since these are both the same, it's going to be 26 different positions or rather characters for those as well. So it's actually 26 squared different unique palendromes we could have that are of length three given that our character set is lowercase a through z. Okay, now let's code it up. And I realize this explanation has actually gone longer than I expected, mostly because of the dry run. I probably didn't need to do that, but I'm going to start with the result that we were talking about. We don't need three characters in this result. I'm actually going to store a pair for every palendrome that we find the middle character and the outer character. And then to get the number of unique palendromes since we know hashets eliminate duplicates, we can literally just take the length of that hash set and return that to get the count. Remember that we're going to have two data structures. A left data structure which I'm going to make a set and a right data structure which I'm going to make a hashmap. But I'm not just going to use a regular hashmap. I'm going to use a counter because Python makes our life so easy. If you don't know how counters work, I explain it pretty well, I think, in this lesson in my Python for goodies course. And it has a bunch of interactive lessons. So, you can actually like write code and test yourself as you kind of go through this course and learn a bunch of other stuff as well. But basically, this is creating a hashmap, counting the occurrences of every character, just like we would want to do. And it's pretty easy to write a loop to do that, but might as well use the built-in uh way to do it. So now I'm going to go through every position in uh the input string and I probably don't even need to do it this way. Let me actually say for character in string s. This is our middle character. So I'll actually call it that mid. And so now what we want to do is first of all say now this character no longer is on the right side because now it's the middle. So we have to remove it from the right side. So that's easy to do. Just take right of this middle character, decrement the count by one. And you might say, we'll take this character and add it to the left side. Left set dot add this middle character. It's true. We want to do that at some point. But before we do that, we want to now check for all the characters that are currently on the left side. Of course, on the first iteration, that'll be empty, but still we want to go through every character on the left side, and we want to check is this character also on the right side. So if right of this character and the count of that is greater than zero then we have found a palrum result.add this pair the middle and the outer character. It's possible that this is a duplicate that's why we have this hash set in the first place. So now I am pretty much done with that. I think this is the entire code. So after we're done checking that then we can take the middle character and add it to the left side. So let's run it. And here you can see it works and it's pretty efficient. If you found this helpful, check out neatcode.io for a lot more.

Original Description

🚀 https://neetcode.io/yt - A better way to prepare for Coding Interviews 🧑‍💼 LinkedIn: https://www.linkedin.com/in/navdeep-singh-3aaa14161/ 🐦 Twitter: https://twitter.com/neetcode1 ⭐ BLIND-75 PLAYLIST: https://www.youtube.com/watch?v=KLlXCFG5TnA&list=PLot-Xpze53ldVwtstag2TL4HQhAnC8ATf Problem Link: https://leetcode.com/problems/unique-length-3-palindromic-subsequences/ 0:00 - Read the problem 0:30 - Drawing Explanation 5:05 - Coding Explanation leetcode 1930 #neetcode #leetcode #python
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Playlist

Uploads from NeetCodeIO · NeetCodeIO · 0 of 60

← Previous Next →
1 Leetcode 149 - Maximum Points on a Line - Python
Leetcode 149 - Maximum Points on a Line - Python
NeetCodeIO
2 Design Linked List - Leetcode 707 - Python
Design Linked List - Leetcode 707 - Python
NeetCodeIO
3 Minimum Time to Collect All Apples in a Tree - Leetcode 1443 - Python
Minimum Time to Collect All Apples in a Tree - Leetcode 1443 - Python
NeetCodeIO
4 Design Browser History - Leetcode 1472 - Python
Design Browser History - Leetcode 1472 - Python
NeetCodeIO
5 Number of Good Paths - Leetcode 2421 - Python
Number of Good Paths - Leetcode 2421 - Python
NeetCodeIO
6 Flip String to Monotone Increasing - Leetcode 926 - Python
Flip String to Monotone Increasing - Leetcode 926 - Python
NeetCodeIO
7 Maximum Sum Circular Subarray - Leetcode 918 - Python
Maximum Sum Circular Subarray - Leetcode 918 - Python
NeetCodeIO
8 Find Closest Node to Given Two Nodes - Leetcode 2359 - Python
Find Closest Node to Given Two Nodes - Leetcode 2359 - Python
NeetCodeIO
9 Concatenated Words - Leetcode 472 - Python
Concatenated Words - Leetcode 472 - Python
NeetCodeIO
10 Data Stream as Disjoint Intervals - Leetcode 352 - Python
Data Stream as Disjoint Intervals - Leetcode 352 - Python
NeetCodeIO
11 LFU Cache - Leetcode 460 - Python
LFU Cache - Leetcode 460 - Python
NeetCodeIO
12 N-th Tribonacci Number - Leetcode 1137
N-th Tribonacci Number - Leetcode 1137
NeetCodeIO
13 Best Team with no Conflicts - Leetcode 1626 - Python
Best Team with no Conflicts - Leetcode 1626 - Python
NeetCodeIO
14 Greatest Common Divisor of Strings - Leetcode 1071 - Python
Greatest Common Divisor of Strings - Leetcode 1071 - Python
NeetCodeIO
15 Shortest Path in a Binary Matrix - Leetcode 1091 - Python
Shortest Path in a Binary Matrix - Leetcode 1091 - Python
NeetCodeIO
16 Insert into a Binary Search Tree - Leetcode 701 - Python
Insert into a Binary Search Tree - Leetcode 701 - Python
NeetCodeIO
17 Delete Node in a BST - Leetcode 450 - Python
Delete Node in a BST - Leetcode 450 - Python
NeetCodeIO
18 Shuffle the Array (Constant Space) - Leetcode 1470 - Python
Shuffle the Array (Constant Space) - Leetcode 1470 - Python
NeetCodeIO
19 Fruits into Basket - Leetcode 904 - Python
Fruits into Basket - Leetcode 904 - Python
NeetCodeIO
20 Number of Subarrays of size K and Average Greater than or Equal to Threshold - Leetcode 1343 Python
Number of Subarrays of size K and Average Greater than or Equal to Threshold - Leetcode 1343 Python
NeetCodeIO
21 Naming a Company - Leetcode 2306 - Python
Naming a Company - Leetcode 2306 - Python
NeetCodeIO
22 As Far from Land as Possible - Leetcode 1162 - Python
As Far from Land as Possible - Leetcode 1162 - Python
NeetCodeIO
23 Shortest Path with Alternating Colors - Leetcode 1129 - Python
Shortest Path with Alternating Colors - Leetcode 1129 - Python
NeetCodeIO
24 Minimum Fuel Cost to Report to the Capital - Leetcode 2477 - Python
Minimum Fuel Cost to Report to the Capital - Leetcode 2477 - Python
NeetCodeIO
25 Count Odd Numbers in an Interval Range - Leetcode 1523 - Python
Count Odd Numbers in an Interval Range - Leetcode 1523 - Python
NeetCodeIO
26 Contains Duplicate II - Leetcode 219 - Python
Contains Duplicate II - Leetcode 219 - Python
NeetCodeIO
27 Path with Maximum Probability - Leetcode 1514 - Python
Path with Maximum Probability - Leetcode 1514 - Python
NeetCodeIO
28 Add to Array-Form of Integer - Leetcode 989 - Python
Add to Array-Form of Integer - Leetcode 989 - Python
NeetCodeIO
29 Unique Paths II - Leetcode 63 - Python
Unique Paths II - Leetcode 63 - Python
NeetCodeIO
30 Minimum Distance between BST Nodes - Leetcode 783 - Python
Minimum Distance between BST Nodes - Leetcode 783 - Python
NeetCodeIO
31 Design Hashmap - Leetcode 706 - Python
Design Hashmap - Leetcode 706 - Python
NeetCodeIO
32 Range Sum Query Immutable - Leetcode 303 - Python
Range Sum Query Immutable - Leetcode 303 - Python
NeetCodeIO
33 Binary Tree Zigzag Level Order Traversal - Leetcode 103 - Python
Binary Tree Zigzag Level Order Traversal - Leetcode 103 - Python
NeetCodeIO
34 Middle of the Linked List - Leetcode 876 - Python
Middle of the Linked List - Leetcode 876 - Python
NeetCodeIO
35 Course Schedule IV - Leetcode 1462 - Python
Course Schedule IV - Leetcode 1462 - Python
NeetCodeIO
36 Single Element in a Sorted Array - Leetcode 540 - Python
Single Element in a Sorted Array - Leetcode 540 - Python
NeetCodeIO
37 Capacity to Ship Packages - Leetcode 1011 - Python
Capacity to Ship Packages - Leetcode 1011 - Python
NeetCodeIO
38 IPO - Leetcode 502 - Python
IPO - Leetcode 502 - Python
NeetCodeIO
39 Minimize Deviation in Array - Leetcode 1675 - Python
Minimize Deviation in Array - Leetcode 1675 - Python
NeetCodeIO
40 Longest Turbulent Array - Leetcode 978 - Python
Longest Turbulent Array - Leetcode 978 - Python
NeetCodeIO
41 Last Stone Weight II - Leetcode 1049 - Python
Last Stone Weight II - Leetcode 1049 - Python
NeetCodeIO
42 Construct Quad Tree - Leetcode 427 - Python
Construct Quad Tree - Leetcode 427 - Python
NeetCodeIO
43 Find Duplicate Subtrees - Leetcode 652 - Python
Find Duplicate Subtrees - Leetcode 652 - Python
NeetCodeIO
44 Sort an Array - Leetcode 912 - Python
Sort an Array - Leetcode 912 - Python
NeetCodeIO
45 Ones and Zeroes - Leetcode 474 - Python
Ones and Zeroes - Leetcode 474 - Python
NeetCodeIO
46 Remove Duplicates from Sorted Array II - Leetcode 80 - Python
Remove Duplicates from Sorted Array II - Leetcode 80 - Python
NeetCodeIO
47 Maximum Twin Sum of a Linked List - Leetcode 2130 - Python
Maximum Twin Sum of a Linked List - Leetcode 2130 - Python
NeetCodeIO
48 Concatenation of Array - Leetcode 1929 - Python
Concatenation of Array - Leetcode 1929 - Python
NeetCodeIO
49 Symmetric Tree - Leetcode 101 - Python
Symmetric Tree - Leetcode 101 - Python
NeetCodeIO
50 Check Completeness of a Binary Tree - Leetcode 958 - Python
Check Completeness of a Binary Tree - Leetcode 958 - Python
NeetCodeIO
51 Construct Binary Tree from Inorder and Postorder Traversal - Leetcode 106 - Python
Construct Binary Tree from Inorder and Postorder Traversal - Leetcode 106 - Python
NeetCodeIO
52 Find Peak Element - Leetcode 162 - Python
Find Peak Element - Leetcode 162 - Python
NeetCodeIO
53 Accounts Merge - Leetcode 721 - Python
Accounts Merge - Leetcode 721 - Python
NeetCodeIO
54 Binary Tree Preorder Traversal (Iterative) - Leetcode 144 - Python
Binary Tree Preorder Traversal (Iterative) - Leetcode 144 - Python
NeetCodeIO
55 Binary Tree Postorder Traversal (Iterative) - Leetcode 145 - Python
Binary Tree Postorder Traversal (Iterative) - Leetcode 145 - Python
NeetCodeIO
56 Number of Zero-Filled Subarrays - Leetcode 2348 - Python
Number of Zero-Filled Subarrays - Leetcode 2348 - Python
NeetCodeIO
57 Minimum Score of a Path Between Two Cities - Leetcode 2492 - Python
Minimum Score of a Path Between Two Cities - Leetcode 2492 - Python
NeetCodeIO
58 Sqrt(x) - Leetcode 69 - Python
Sqrt(x) - Leetcode 69 - Python
NeetCodeIO
59 Successful Pairs of Spells and Potions - Leetcode 2300 - Python
Successful Pairs of Spells and Potions - Leetcode 2300 - Python
NeetCodeIO
60 Optimal Partition of String - Leetcode 2405 - Python
Optimal Partition of String - Leetcode 2405 - Python
NeetCodeIO

This video teaches how to solve the Unique Length 3 Palindromic Subsequences problem on Leetcode using Python, utilizing data structures such as hash sets and hash maps to count unique palindromic subsequences in O(n) time. It demonstrates the use of a hash set and a hash map to store characters on the left and right sides, and a constant time lookup to find matching characters. By following this lesson, viewers can improve their problem-solving skills and learn how to implement efficient algori

Key Takeaways
  1. Initialize the left side with a hash set and the right side with a hash map
  2. Add characters to the left side and decrement their counts in the right side hash map
  3. Remove characters from the right side and decrement their counts in the right side hash map
  4. Store only the left and middle characters of each palindrome in the hash set
  5. Use a set to count unique palindromes on the left side
  6. Use a counter to count character occurrences on the right side
💡 The key insight of this video is the use of a hash set and a hash map to store characters on the left and right sides, and a constant time lookup to find matching characters, which allows for an efficient solution to the Unique Length 3 Palindromic Subsequences problem.

Related Reads

📰
KMP Algorithm (Knuth-Morris-Pratt): The Smart Way to Perform String Matching in O(N)
Learn the KMP algorithm for efficient string matching in O(N) time complexity and improve your coding skills
Dev.to · Jaspreet singh
📰
Every Backtracking Problem Is the Same Three Lines. I Just Couldn't See the Tree.
Master backtracking problems with a simple three-line approach to improve problem-solving skills in coding interviews and challenges
Dev.to · Alex Mateo
📰
DSA From Zero to Hero #3: Sliding Window (Fixed Size) Explained With a Java Example
Learn to solve subarray problems efficiently using the sliding window technique, a crucial skill for software engineers and data scientists
Medium · Programming
📰
Two Pointers & Sliding Window: Turn O(n²) Into O(n)
Learn to optimize algorithms from O(n²) to O(n) using Two Pointers and Sliding Window techniques
Medium · Programming

Chapters (3)

Read the problem
0:30 Drawing Explanation
5:05 Coding Explanation
Up next
Stump Grinder Carbide Wheel Grinds Hardwood To Chips
Innoforge Studio
Watch →