Sliding Window Algorithm for Tech Interviews - Full Course

💬 Chat with this Video
Ask anything about this video…
The sliding window algorithm is a powerful technique used to optimize problems involving arrays or strings by converting nested loops into a single pass. This course is designed to help you master this essential pattern, providing you with the intuition needed to tackle common technical interview challenges. Alvin created this course. Hey programmers, and welcome to our course on the sliding window algorithm. If you're preparing for technical interviews and want to master this common topic, then you're in the right place. If we haven't met before, hey, I'm Alvin. I'm an ex-Google software engineer, and for the last decade, I've taught thousands of engineers how to ace their interviews and land their dream job. In the next 2 hours, I'll teach you everything you need to know about the sliding window algorithm. That means we'll cover both the fixed-size and variable-size versions of the algorithm. In this course, we'll learn by completing a series of problems, the same problems that you could see on your tech interviews. Don't worry, though. I've specifically chosen each problem so that it ramps up gently. We'll start with the basics, and then slowly increase the difficulty so that you never feel lost. By the end of the course, you'll be solving some pretty advanced problems. For each problem, we'll use a two-step approach to learning. First, we'll come up with a plan on the whiteboard. I think visualizing algorithms is really key to understanding them. So, here's where we'll illustrate the steps of our logic and address any edge cases. From there, we'll head into my editor, where we'll implement the code and pass those test cases. I'll be coding in Python, but you can feel free to follow along in your favorite language. All right, I'm super excited to teach you sliding window. So, without any further delay, let's jump into our first problem. Hey programmers, Alvin here. Right now, I want to go over the approach we can use for this max subarray sum size K problem. So, in this problem, we're going to need to learn a new strategy. What does this problem asking us to do? We're we're to take two different inputs. We're going to take in an array of some numbers as well as a size K, which is just another number. Here, what we want to do is consider sub arrays of size K. So, because right now K is four, it means I look at sub arrays with four elements inside. I do bear in mind that a sub array is going to be a continuous a collection of four elements here. So, here I have highlighted one possible sub array. If I consider the sum of these elements in the sub array, their sum is 33. What I want to do is consider all such sub arrays with four elements. For example, this sub array at the very front has a sum of 16. This sub array somewhere in the middle has a total sum of 43. What I want to do is return the maximal sum I can get uh with any sub array of length four. The answer here would indeed be the 43. That's the biggest sum of sub arrays uh that contain four elements. All right. So, let's start discussing a strategy for this one. It's really important that we do notice that we don't need to consider every possible sub array of any size. What we want to do is only consider sub arrays of this target size. Here, our target is four. So, what I can do is just start the very beginning of the array and move this window, right? So, here I have the first four elements. After that, I want to consider the next four elements, and so on and so forth. And that of course gives us our classic sliding window strategy, which is what we're going to explore in this upcoming module. So, let's discuss the logic that we're going to use for the sliding window algorithm. So, we're going to start our window at the very beginning of the array. That makes sense. And because in this problem we want to consider uh the maximum sum, I'm going to create a variable to track the maximum I've seen so far. This should be very familiar. You can go ahead and initialize this variable to something logical like negative infinity, right? Cuz I want it to be replaced over time. Negative infinity is never going to win when I compare a maximum. So, now that I'm looking at this very first window containing my four elements, I want to just take their sum. So, in other words, just add up 1 + 4 + 1 + 10. That gives me the sum of 16. I should just compare this 16 to my current maximum. And of course, I should replace that negative infinity because 16 is greater than negative infinity. So, right now the max sum I have so far is 16. At this point, I could just slide my window. Now, I look at the next four elements. Again, just add up all the elements inside. 4 + 1 + 10 + 25 gives me a sum of 40. And I do that max value logic. I compare 40 against my maximum sum of 16. 40 is greater, so I go ahead and replace that as the max sum. Slide my window over to the right a little bit. And again, I add up these four elements. You take the sum of 1 + 10 + 25 + 3, you get 39. 39 is not bigger than my maximum sum so far, so I don't replace it. You should still move your window over, though. Looking at my next window, I do 10 + 25 + 3 + 5. That gives me a sum of 43. And of course, 43's bigger than my current maximum sum of 40, so I replace it with the 43. This algorithm will just continue for the remaining elements. This next sliding window has a sum of 33, which is not going to replace my 43. And finally, this very last window has a total sum of 34, which is not going to replace my 43. Because I'm done looking at all possible sub arrays of size four, I can just return this as my final answer, right? 43 is the maximum sum of any sub array containing exactly four elements. So, let's go ahead and start to think about the complexity of this algorithm, right? So, I'll define my terms. Here, I have two inputs, right? I have my numbers array, as well as my size K. So, let's say N is the length of the array. Let's say that K is the sub array size that we're targeting. What I want to do is start thinking about my possible pieces of my complexity here. So, the first part would be the number of sub-arrays, right? How many sub-arrays am I looking at? In other words, how many windows do I have to look at here? I'm going to tell you that the number of sub-arrays is given by roughly n minus k. So, how can we reason that out over here? Well, of course, the sub-arrays are really just every little window I looked at over here. We know that we're going to slide this window throughout the nums array. And so, think of this total number of elements, total number of elements in our nums array, we just said was n. And of course, you're going to be sliding this window to the right, like so. And as you slide it, this is the farthest you can move it to the right, right? Because of course, your window that you consider contains k elements. That'd be over here. So, you really need to stop sliding your window once you're k elements from the right edge of the array. In other words, you can't look outside of bounds here, right? For example, if you had your window starting at the element zero, then your last two elements are going to be out of bounds. So, that wouldn't be a valid sub-array to consider. That's why I'm saying you need to stop sliding your window once you're k elements from the end of the array. So, the total number of sub-arrays you consider is really just n minus k. All right. And beyond that, if that's going to be the number of sub-arrays we look at, how much work do we do per sub-array? That'd be the next piece of my complexity here. Well, the amount of work you do per sub-array is really going to be k. What does that really mean? Well, whenever we looked at some window, for example, the one I have on the screen right now, you had to add up all of the elements inside of it. And of course, I know there going to be k elements inside of that little window. So, you need to do k work for every sub-array you consider. So, how can you get the total time complexity from this? Well, you could just multiply these two quantities together. So, your complexity in its Its form is going to be n minus k times k. This quantity is just the number of sub arrays multiplied by the amount of work per sub array. Let's go ahead and simplify this time complexity and to do that you can just start by multiplying that x across your terms here. So if I multiply those things, I get nk minus kk. Now we can simplify this even more. Given in the problem, they tell us that n is greater than or equal to k. In other words, the size of the sub arrays you're going to be looking at, it can't ever be bigger than the original array, right? Cuz that really wouldn't make any sense. And because we have this guarantee that n is greater than or equal to k, we can simplify even further. This means that nk is certainly bigger than kk. And so I can drop the kk from my complexity. So right now I'm saying that the time complexity of this algorithm so far is just O of nk. And this is a fine strategy in that it gives us the correct result, but it's not the optimal strategy. Really, this is just our naive sliding window time complexity. So I want to actually discuss an algorithm that can perform faster than this. And don't worry, we're still going to utilize sliding window, but we're going to manipulate things a little bit here. And so let's go ahead and think about uh the logic here a bit. So when I consider a window like this one for example, I know I have to add up all of the elements inside. And that definitely contributed to my complexity, right? That's where the k comes from in my complexity of nk right now. So after I consider uh this sub array, when I slide my window to the right, the next sub array I consider is this one. Here I'm going to mark it in green. And I'll also have arrows pointing to the elements I need to add up for that little window. Also in green. It's pretty clear to me that I have a lot of overlap in these two windows, right? Everything in the middle overlaps. And so what I want to do is avoid uh recomputing those into the sum. Right? And so, how can we actually redesign the algorithm here? It's going to start in a very similar way. You're going to begin with your window all the way to the left. That makes sense. And what you want to do over time is also track a current sum. So, previously I just had a max sum. Now, I want a current sum. Right? And because you're at the very beginning of the algorithm, just go ahead and uh find the total sum of this initial window. So, just do 1 + 4 + 1 + 10. That gives you a total sum of 16. And from here, you just continue your basic max value logic. Because I don't have a maximum stored so far, this current sum of 16 is going to be my initial maximum. Great. And now, here is when I would want to move my window to the right by one position. And when I move my window to the right by one position, I know that this trailing element over here is not going to be part of my window anymore. Right? So, what I can do is just subtract it from my current sum. So, my current sum is 16. I subtract the trailing element of one. That gives me a current sum of 15. And as I move my window to the right, that also means that this element over here, this leading element of 25, it's going to be now part of my window. So, I should add that 25 into my current sum. 15 + 25 gives me 40. And that 40 is going to be the sum of that next window right here. Right? And double-check your work here. 4 + 1 + 10 + 25 is indeed going to give us 40. Great. So, at this point, I can continue my max value logic. I compare the 40 to the 16. Of course, the 40 is going to replace it. And I continue this algorithm. The key is I'm going to subtract my trailing element and add my leading element. So, I subtract the four from my current sum. Gives me 36. And don't forget to add your leading element of three. 36 + 3 gives me 39. And that would be the sum for this next window over here. Great. Do your max value logic. 39 is not going to replace the 40. So, I continue sliding to the right. Again, I subtract the one here, but also add the five, giving me my next window of 43. 43 is bigger than the max right now of 40, so I replace it. And go ahead and keep sliding your window to the right. Here, I need to subtract the 10 and add a zero, which means that my current sum is going to be 33. 33 is not bigger than 43, so it stays. Go ahead and slide your window one more time. I subtract 25 and add 26. That gives me a current sum of 34. 34 does not replace the 43. At this point, I'm done sliding my window all the way to the right. So, the final value of my maximum sum would be the true maximum of any subarray inside of my original array. Uh so, with that small change, let's go ahead and talk about the complexity of this new algorithm. So, the number of subarrays that we consider is really the same. It's still going to be n minus k, right? We still slid the window uh throughout the nums array. So, it's still n minus k. What did change was the amount of work per subarray. If you think about it, what did we actually do? We only did two steps per subarray. So, just recall, whenever we considered uh some window, all we had to do to get to the next window to the right was subtract the trailing element and add the leading element into our current sum. So, the amount of work is really just two. What we're really saying is we just have a constant amount of work uh no matter how big the window is, right? Here, our window is a size of four, but imagine your window was a size of 10. You would still only need to subtract a single trailing element and add a single leading element. So, that's really a great optimization here. So, let's talk about the total complexity of this new and improved algorithm. If you want to go nice and slow here, you can still say uh that the time complexity is going to be the number of sub arrays multiplied by the amount of work per sub array. Here we do n - k * 2, but of course you can simplify things. Two is just a constant factor, so you can drop it completely. And like we said, here we know that n is greater than or equal to k, so I can simplify this quantity even more. It's really just going to be an O of n time complexity for this. This would be the perfect optimized sliding window time complexity. Naturally, if you compare this to our initial naive solution, our previous version was n * k, but our new and improved version is just n. If you want to talk about the space complexity here, when you go ahead and code this up, you're not really going to use any scaling data structures. You're just going to have a handful of variables that you're going to do some max value logic as well as some addition into. So your space complexity here is just going to be constant. All right. So now that we have a good idea about the plan we want to use, it's time to code it up. In the next section, I'm going to hop into my text editor, we can implement the code and run some test cases. It's really important that you follow along. If you head to the video description, you'll find timestamps along with links to the coding workspace that I'm going to be using. So feel free to code along there. Or if you're more comfortable, feel free to edit in your own text editor. I'll be sure to pull up test cases on the screen so you can also test your work. I'll be coding in Python, but in our workspace, you can feel free to switch the language to Java, C++, or JavaScript as well. Hey programmers, Alvin here. Right now, let's go over the Python walk-through for this max sub array sum size k problem. So definitely make sure you watch the approach video first, because we're going to learn a new strategy. What we want to do is apply the sliding window algorithm. And so, I'll solve this in two ways. I'll solve it with my baseline sliding window algorithm, and then I'm going to optimize it to get the most optimal time complexity here. And so, to get going, let's go ahead and implement that very first version. So what I want to do is uh create some variables here. In the long run, what I want to do is return the maximal sub array sum. So I'm going to create a max sum variable here. Like we expect, it's really important that we default this to a nice value. I'm going to set it equal to negative infinity. Cuz I know that anything I actually find is going to replace this value. Great. Then from here, I want to start looking at my window. So, I'm going to create a nice little for loop here. I'll say for I in range, and you're going to start at the very beginning of your nums array. And you want to go toward the end, but you have to be sure that you don't go out of bounds. So, this gets a little tricky really to figure out exactly where your stopping condition is. You want to just look at an example, but I know that's going to be up to the length nums minus K. But then also a plus one here. I'll kind of explain why that is so we avoid any off-by-one errors. So, I'll bring in the first example. So, let's just quickly check the boundary of this loop here. So, it makes sense that I'm starting at index zero because your first window needs to start here. And notice that my K is three. So, I want to grab three elements at a time. So, the very first iteration is going to be right here, which is great. Of course, on the other end, I know my very last iteration needs to be this over here, right? Do you bear in mind that the length of this nums array right now, there are seven elements. So, here I'm doing 7 - 3, right? Cuz K is three. 7 - 3 gives me four, right? And then I do a plus one to it. So, that gives me five. If I count my indices, they go 0, 1, 2, 3, 4, and 5. I do bear in mind that the range function within Python, the second argument is exclusive. So, although I'm passing in the index of this element over here, it's going to be excluded in my range. So, really the last iteration starts here at the eight and goes all the way through the end, giving me my very last window. That's why I need to do the plus one over here, just to make sure that I end in the right scenario. Great. And so from here, now I'm able to iterate through all of my windows and I'm sliding them to the right. In the naive solution, all you would do is take the sum of everything within that window. So what I can do is create a variable, I'll call it my current sum. And I'll use the built-in sum function in Python. It's really useful here. I could say sum, I could pass in just a sub array. So the sub array that I want to grab is going to be from nums. It's going to start index I and [snorts] go to the next K element. So it'll be I plus K over here. Great. And so again, we could do a little spot check if you like. Let's say I'm somewhere in the middle of this algorithm. Let's say that right now I is pointing to this element over here, right? So it has index two, cuz I go 0 1 2. I know I want to grab these three elements, right? So what I'm saying is start at index two, which is correct, and go all the way up to but not including two plus three, cuz again my K is three. So 2 + 3 is 5. So if I count my indices, 0 1 2 3 4 5, right? And the second argument into this Python list slice is going to be exclusive, so I won't include this four, which means I grab the one, the -9 and 8. So that works out nicely here. Now that you have the current sum, go ahead and just do your max value logic, right? So I'll go ahead and check if my current sum it's bigger than the max sum, then I also replace it, right? Max sum should be equal to the current sum. Then at the very end, return your max sum. Do you bear in mind that I'm only returning the maximum after I'm done looking at every possible sub array here. So I'll comment this out and I'll give it a test run. This is going to be our naive solution. It does give you some correct results for the most part, but it will time out for some larger test cases. Great. So let's go ahead and optimize this now using what we spoke about in the approach video. So, I'll go ahead and create my version two of this. Which will be a little faster. So, I know that in this version two, what I want to do is have two variables. I want the current sum of my window, as well as the max sum. So, I'm going to create that over here. Set equal to sum. I'm going to grab the very first window, which would be nums. From the very beginning all the way through the first K elements. So, if K is three, this would grab the sum of the first three elements here. Great. And so, from here, uh what I want to do is now go ahead and form the general algorithm. Which means I need to iterate through everything else. So, I'm going to say for I in range. So, I'm starting at the very first index and go all the way through the last index you want to hit, which would be the same in the condition from before. So, I'm grabbing this. This would be my uh very last window. So, really because my K is three in this particular example, I start grabbing at this window. My last window is going to be this one. And you just need to do the plus one here to account for that in Python, cuz this argument is going to be exclusive. Cool. And so, from here, here's where we work in that uh fixed number of steps to compute the next window, right? So, instead of actually calculating the sum of everything in between, I want to make sure I only do a constant number of operations here. In the approach video, we said that we would subtract our trailing element and add our leading element into our current sum. So, I know I already considered my initial window right here, right? I just manually grabbed the sum of the first three elements. So, in this particular example, my current sum would be 4 + 2 + 1, which would give me a seven, right? And so, current sum is seven, you will want to actually initialize that into your max. So, I'll say my max sum is going to be my current sum right now. It's really important for this like optimized version of our sliding window algorithm, you will have to treat the very first window in a special way, right? Your first window is handled with this code and all subsequent windows are going to be handled by uh this for loop here. And so, in this for loop, what I want to do is now help compute the uh next window. So, what I can do is I can take my current sum and I want to subtract from it the element that is leaving the window. So, this is my current window, my next window is going to be this guy over here, which means that the four needs to leave. So, I better subtract that four from my current sum. So, here I'll say current sum minus equal nums at square bracket I, right? Cuz again, that would be the start of the window here. And on the flip side, you want to add into it the element that's entering the window. So, if this is my current window and this is going to be my next window over here, it would be this -9, right? That just entered my window. And you can do that by doing I + K here. It makes sense, you're just grabbing K elements after your current index. There is a lot of indexing that we do in an algorithm like this, so feel free to pause the video and double-check that the math works out for yourself here, right? But, trust me on this one, this is going to work. And now that I'm calculating my current sum properly, here's where I would do the check. And now I want to check, all right, if this next window, if the current sum is greater than the max sum. And if it is, then I just do my max value logic. So, your max sum equal to the current sum. And after you do this for every remaining window, just return what's ever in the maximum. Awesome. So, let's go ahead and test this out now. So, it looks like I have a little error here and a pretty common one at that. I'm going to add out of bounds, so I do want to check things here. Probably have an off-by-one error. And if I'm getting my list index out of range, that probably either mean toward the beginning of my uh nums array or at the end of it. And looking at this code when I do I + K, that's me looking ahead in the list. So, I'm probably looking outside of the right edge of my list here. So, let's go ahead and think about like the last iteration of our code. And it might have to do with this plus one I have here. I probably don't need it. I know that I copied this part of the range from this previous code over here, but the logic is probably a little different, so that's probably why it's erroring out. And so, I know that this is the scenario where my I is pointing to the start of the very last window. Yeah, and I can already see because here if I do my current sum minus equal nums I, means I'm going to be subtracting the eight, right? And then I'm going to try to add the next incoming element. Here I take my current position and then I add three to it, right? Because K is three. So, if my current position's here and I add three to it, I go one, two, and three is out of bounds, right? Because this is really my very, very final window, I don't actually need to look ahead anymore. Right? So, that's why this code is going out of bounds. So, in this optimized version, you actually don't need the plus one here. Great. So, let's go ahead and test it out now. There we have our nice solution. The thing I want to really call attention to is how we set up this optimized version. It really is kind of like a two-parter to me. First, you have to explicitly handle your initial window, right? Here I just went ahead and grabbed the immediate sum of the first K elements. And then the for loop is going to handle all subsequent windows. And if you do this in a smart way, you should do is only a fixed number of operations to calculate the next window. In particular, here I just did a simple subtraction of my trailing element and also a simple addition of my leading element, right? Because I only have two steps for every iteration of the for loop, I get that overall O of N run time. Here I actually ended up using some extra space just for convenience. Here when I did the built-in sum function call, I'm actually passing in a copy of a sub array, which of course takes a little bit of space. But it's really easy to optimize this away if you really cared about getting down from like a linear space complexity or O of N space complexity here down to a constant space complexity. I can show you that really quick. It's really nothing fancy. You just say your current sum equal to zero and you just have to manually compute this, right? So you can say for I in range and start index zero and go through the first K elements, so just K over here. Nice. And then just take your current sum and add into it nums of I. So here you wouldn't be using any additional space. Let's test it out. To be honest, if I was on an interview, I'd probably still use uh the built-in sum function just cuz it's really convenient. And generally, we're okay with uh trading off some linear space as long as we get the optimal time complexity. All right, programmers, that's all I got for this one. Definitely take some time here and make sure you feel comfortable with the indexing. A lot of opportunity for off-by-one errors, as you can see, the one I just made. And don't worry, we're going to be able to practice this concept further in the upcoming module. Hey programmers, Alvin here. Right now, let's go over the max sub array product size K problem. This is going to be a nice build off of our very last problem, uh practicing the sliding window algorithm. So here we're still given an array of some numbers as well as a size K. What we want to do is consider uh different sub arrays of size K or size four here and find the one with the maximal product. So for this very first sub array, it has a total product of 24. Of course, the product is just going to be what we get when we multiply all the elements together. If I look at some other sub array here, this one at the very end has a total product of -780. This one in the middle has a product of 270. And this would actually be the answer I want to return. In other words, you should return 270 because it's the maximum product we can get of any such subarray. Great. So, it should be pretty clear that this is going to be a small variation of the very last problem. So, we can practice that sliding window. So, like you expect, because I want to find the maximum here, just maintain a max product variable. So, we'll start by talking about the naive sliding window strategy just for some nice review. So, now that we have my window at the very front here, we would need to multiply these elements together, right? So, their total product is 24. Because you have no maximum product so far, that will be your current maximum. Then you just slide your window to the right. You get the product of these four elements. You'll get -72. That's not going to replace the maximum so far. So, you go to the right. Here you have a total product of -54. Still don't replace it. Here you have a total product of 720. So, you would replace your max product. This algorithm will continue all the way through to the end of your array here. And of course, 720 would be your ultimate answer. We already said how this algorithm so far has a time complexity of n * k, right? It's really the same analysis that we did last time. Uh the issue with this algorithm so far is that factor of k, right? Because we're just naively multiplying all of the elements within any particular window, we'll have to do k operations for every window that we look at. And so, what I want to do is get rid of that k. And here's the optimization. Instead of looking at every single element within your window, you don't want to do that. Instead, just try to look at two elements at a time. So, here's what I mean. We're going to start our algorithm at the very front. And in addition to our max product variable, we're also going to track our current product. This is going to be the current product of our window so far. Because I'm at the very start of my algorithm, you just need to process those first elements. In other words, just go ahead and multiply them together. In other words, 1 * 4 * 1 * 6 gives me 24. I can go ahead and set that 24 as my maximum thus far. Now that you want to slide your window to the right, what you could do is just remove that trailing element uh from your current product. Here you want to do the opposite operation. So, over time I was calculating the product, in other words, multiplying things together. When I want to remove some number from the product, I just divide, right? Cuz division is the opposite of multiplication. So, if I do my 24 / 1, it really keeps my current product at 24. On the flip side, I have this uh number of -3 that's going to enter my product within the next window. So, I need to multiply by -3. So, I multiply my current product of 24 by -3 gives me -72. And that would indeed uh be the total product of that next window here. In other words, 4 * 1 * 6 * -3 is -72. -72 is not going to replace my maximum, so I can continue sliding my window. I need to remove this four uh from my current product, which means I should divide it, right? So, I divide four into my -72. That gives me -18. And then also be sure to multiply your leading element. So, I do 3 * the -18 here. Giving me my next window of -54. Again, -54 is not going to replace my max product. So, I keep sliding. I go ahead and divide by one and multiply by -5. That means my next window has a total product of 270. 270 would replace my maximum product, cuz of course it's bigger. So, my max product right now is 270. Keep sliding to the right. In my next frame, what I do is divide by six and multiply by two, giving me 90. 90 is not bigger than my max product, so I keep going to the right. Here I need to divide by -3 and multiply by 26. And this final window has a total product of -780. Of course, that's also not going to replace my max product. Now that I'm done sliding my window all the way through the right, the final value I have inside of my max product would be my final answer. So, the answer here we should return is 270. Like we said previously, this is our optimized sliding window strategy. The time complexity for this is just O of N, right? When we considered uh finding the next product that we have to look at, we only did a constant number of operations, right? What we said was we just divide by our trailing element and multiply by our leading element. That would be true no matter the size of our window, right? Here K is 4, but even if K was 100, you would still only need to divide by one thing and multiply by another thing. Time complexity here is O of N. Space complexity is also going to be O of 1 just like last time. Right. So, what I want you to do is try to code up this one on your own. It's going to be very similar to our last problem. It's good practice for getting this sliding window algorithm down pat. If you get stuck, I'll see you in the walk-through video. Hey programmers, Alvin here. Right now, let's go over the Python walk-through for this max subarray product size K problem. So, this is just going to be a small variation of our last problem, uh giving us another chance to practice the optimized sliding window algorithm. So, I'll jump right in. I'm going to go immediately to the optimized version. Going to create my current product variable. Cuz I know the first step is to just process the first K elements, right? Let's get that first window. So, I'm going to iterate for we'll say I in range. You know, go from zero just to the first K. And what I want to do is multiply into the product multiply this current element. So, nums at I. Great. So, now that I have the product of the first window, I can set that as the max so far. Nice. I'll bring in an example here. Just so we can talk about it a bit. So, we already handled this initial window. Now, I need some other loop that's going to slide this window all the way to the right. So, I could consider every sub-array here. So, I'm going to start still at index zero as I iterate here. This time I want to stop around length of nums minus K. This has the effect of stopping uh roughly K elements from the end of the list, right? Like we said in the approach video, the important thing is that we do a constant number of steps as we slide our window, right? So, right now my window's here. If I want to go to the next window, I need to remove the four from my product, but also incorporate -9 into my product. And I need to use the appropriate operations for that, divide out that trailing element of four. So, I could say divide equals nums at index I. On the flip side, I want to multiply in multiply in nums at I plus K. Nice. So, doing a quick check here, if I is zero, that means I would remove four from my product. That's good. And if I is still zero, and I do K is three, that means I'm indexing at three. So, it's zero, one, two, three. I would be multiplying in my -9, which is also good to get my next window. From here, you just do your classic max value logic. So, check if your current is bigger than your max product. And if it is, you can replace it. So, can say max product equals current product. Nice. Then after you're done sliding your window, just return the maximum. This will be our optimized version. Really, really similar to our last problem. Just wanted to give you another look at this algorithm. Great. Another way you could write this is maybe using the built-in max function. So, we could say max product equals just call max, pass in those two variables. It would be the same way to write this, just in a one-liner. Really, the important thing here is that you go for that O of N run time. Right? I have K iterations in this for loop, and then the rest of that uh input array is going to be iterated in this for loop here, for which I only do a constant number of operations each time. Notice that I did not nest any loops here. Hey programmers, Alvin here. Right now, I just want to give us a quick summary of the algorithm that we've been implementing lately. So, for the last two problems, we've been exploring the sliding window strategy. In particular, we've been looking at the sliding window with a fixed size K. And so, naturally, this is good for considering sub-arrays or sub-strings of one size. The one size being that K argument. And like I'm saying here, not only is this strategy good for considering sub-arrays within a larger array, but it's also great for considering sub-strings within a larger string. And we'll see that later on. It really doesn't change in any particular way. And so, let's summarize the steps that we've been using so far. And I think for the most part, this is going to be a good recipe whenever you're applying the sliding window with a fixed size strategy. And so, your first step is going to be to process the first K elements into some collection. What you've noticed thus far is that this process actually happens separately. Usually, we have an initial uh loop that's going to process the first K elements to get our initial window rolling. And then from there, what you want to do is move the window to the right using some other loop. And this is the really key part. In the long run, you want to go for a roughly linear uh time complexity. And so, you want to keep the number of steps as you move the window, you want to keep that constant. So, your first move is going to be to remove the trailing element from the collection, right? In other words, the thing that is leaving the window, you need to remove it from your collection, whether that's your a product or your sum or some other data structure like we'll see later on. In addition to that, because you're moving your window to the right, that means some leading element needs to be added to the collection. Generally, these remove and add operations should be opposites, right? So, in the case of calculating a total sum, your remove is going to be subtraction and your add is going to be addition. In the case of taking some large product, your remove is going to be division and your add is going to be multiplication. And do try to visualize things over here, right? So, when I have the window like we've been exploring in the last few problems, your trailing element is really the last thing that's about to leave your window and then your leading element is about to enter your window. So, of course I'm going to slide this orange window to the right. So, now that you've established your new window, you just do the business logic and that really depends on the context of the problem. So, thus far we've done things like taking the max. Obviously, this wouldn't change much if you took the minimum and of course I'm going to show you a lot more variations as we move through the course. But the great thing is with the sliding window of fixed size strategy, this is my general recipe and it works pretty great. After you do the business logic, you need to go back to step number two, keep moving your window to the right and you just do steps two and three again and again until you've processed uh your entire array. All right, so that's our little summary of the sliding window with fixed size K strategy. We'll go ahead and practice some more problems right now and of course we're going to increase the difficulty, that way you're ready for your interviews. Hey programmers, Alvin here. Right now I want to go over the approach we can use for the subarray target sum size K problem. In this problem, we're given three arguments. Our first argument is just going to be an array of some numbers. Our second argument is going to be a target sum. And our third argument is going to be the size K. Here we're considering uh sub arrays of size three that sum up to seven. What we want to do is count those. So, how many sub arrays of size three sum up to seven? The result here should be five. Because there are five different sub arrays that meet that criteria. Here's the first sub array. Of course, 2 + 3 + 2 is indeed seven and it's three elements, so that's good. Here's number two. Number three. Number four. And then your fifth and final. So, really this is going to be a counting problem for us. But of course, we're going to utilize that sliding window strategy. We know that this problem is a good fit for a sliding window uh because of course, we're considering sub arrays of some fixed size. So, what I want to do to get going is just process the first K elements depending on my window size. So, looking at my uh very first window, here it is. I want to just find the sum of them. Cuz of course, I'm looking for something that matches the target sum in the long run. So, 2 + 3 + 2 gives me seven. We're just going to start by implementing the optimal solution cuz by now, you should be familiar with how it's going to go. So, in addition to the sum of our current window, what I also want to do is keep track of the global count, right? That's going to be the answer I eventually return. Right now, my sum is exactly the target, so I should count it. So, right now my count is one. And I'm ready to move my window. So, I'm ready to slide it to the right. Like we expect, what you want to do is remove your trailing element from your sum and then add your leading element into the sum. So, I do a minus two and a plus two, which really doesn't have an effect on changing the sum, but it is going to be considering the next window. It's still the case that my sum matches the target, so I should also increment my count. And I continue. I do a minus three and also a plus three. So, my sum for this window is still seven. So, I should count it. Keep moving to the right. Here I do a minus two and plus one. That gives me a current sum of six. Six is not equal to the target of seven, so I don't increment my counter. Keep this pattern going. I'm going to subtract my trailing element of two and add my leading element of three. Now my sum is seven. Because seven matches my target, I increment my count. And we're going to continue this logic, updating our current sum and comparing it against our target of seven until we find any matches. You're going to reach your next match right about here. Here you have a sum of seven. It matches your target of seven, so you should increment your count one more time. And once you hit the end of your array, you're done considering all possible sub arrays of size three. You just go ahead and return the counts over here. So by now you should anticipate what the complexity of this is. This algorithm just has an O of N run time, where N is going to be the length of the nums array. Space here is going to be constant. It's really the same analysis that we did for the previous two problems. So what I want you to do is try to code this one up on your own. If you get stuck, you can catch me in the walk through video. Hey programmers, Alvin here. Right now let's go over the Python walk through for the sub array target sum size K problem. So of course we want to continue the theme as of late. We want to solve this one using our sliding window of a fixed size algorithm. And so to get going, I'm going to process my first K elements. Here we still want to find the sum of our sub arrays, but this time we want to count them, so I'll still have my, we'll say, current sum. Initialize it to zero. I just want to process the first K elements. So I say four I in range, go from zero just up to K. And I want to add the element, so current sum plus equals nums at index I. Now along the way I want some count variable which I'm going to return at the very end. And do bear in mind that because you've already processed the first window, you should check the sum of that window and see if it matches the target. So here, I might just use a Python ternary, so I can check, all right. I'll set it I'll set the count equal to one if the current sum is equal to the target. Otherwise, if it's not equal to the target, then initialize it to zero. Cool. Uh for our sliding window with a fixed size the initial window is kind of processed separately, so it's important to make sure you still count it if necessary. And then this code now over here with the for loop is going to handle all other windows, right? So I'll say for I in range, go from zero, go up to length of nums minus K. So this has the effect of stopping K elements from the right edge of my list here. And because I'm dealing with some logic, it's straightforward, just do current sum, subtract out your trailing element, so nums I and then add in your leading element which is at index I plus K. Now you need to work in the specific logic for this problem, which is just checking your current sum, right? Check if the sum of this window matches the target, and if it does, then just count it. So count plus equals one. That's all there is to it. Should be very comfortable with seeing this pattern again. Great, so looks like our code is passing. Something I want to emphasize here is I think the most common mistake for uh some of your sliding window of fixed size problems is to not correctly uh consider the very first window because I handle it specially using this for loop, be sure to check if that initial window sum is already equal to the target, then you want to initialize your count to one, otherwise you set it equal to zero. Cool. Really uh the first window is handled specially in this code, and all subsequent windows are handled in this code. Hey programmers, I wanted to take a quick break to tell you about my complete data structures and algorithms course at structy.net. If you enjoy my teaching style, then I think you'll really love my full course. The sliding window material we've already been working through is actually taken straight from my course at structy.net. There I'll teach you everything you need to know to pass your DSA interviews. There I'll cover sliding window, trees, graphs, dynamic programming, and much more. The best part is I'll be there to explain each problem using the same clear animations and code walkthroughs that you're used to. That's structy.net. I'll leave a link in the video description if you're interested. And now, let's get back into our sliding window course. Hey programmers, Alvin here. Right now, let's go over the approach we can use for this has substring anagram problem. So, in this problem we're given two strings. Here I have a string input greyhounds. I'm also given the target anagram of HOI. H O Y. What I want to do is return a boolean true or false indicating whether or not the anagram is found somewhere as a substring in the larger string here. So, if I look for the characters H O Y, recall that an anagram can have its characters rearranged, right? In this particular example, it is actually the case that I could find an anagram of H O Y as a substring in greyhound. It would be right here, right? Y H O. Recalling that an anagram can have its characters appearing in any order. So, for this particular input, you should return true. Let's take a look at another example. Let's say you're given this string. You're still given the target anagram of H O Y. For this case, you should return false cuz there is no substring that contains an anagram of H O Y. So, if you can't find the substring anagram, you just return false. So, let's talk about the approach here. For this particular problem, what we want to do is consider substrings. Something important about substrings is uh they're going to be a consecutive sequence of characters within a larger string. A substring is just like a subarray. Instead of dealing with an array, you're dealing with a string. We're also checking if the anagram exists anywhere in the string. Here my anagram of h o y has three characters, so I'm looking at substrings of length three. Based on what we've been practicing recently, we know that we can easily get all substrings of length three by just sliding some window throughout my string. So, we're going to solve this using a sliding window strategy. It's going to be a little different though. Notice that this one doesn't deal with any numbers. All right, so we know we need to slide a window to consider different substrings, but how can we come up uh with checking if we actually have an anagram? Well, the key is going to be to use a set. So, recall that your set data structure is something that has constant time uh insertion of elements, and also constant time removal, and constant time lookup. And so, why is using a set a good idea in this problem? Well, a set has some uh very important characteristics. For one, the items in a set are unordered. And that's actually a good fit because an anagram could have its characters appearing in any order. Besides that, I also know that items in a set are necessarily going to be unique, which is actually an assumption that they give us in the problem. They tell us that the characters within the string, as well as the characters in the anagram, are all going to be unique. So, those two characteristics uh do fit nicely with the usage of a set. All right, so how would this algorithm flow? For the most part, it's going to be our uh sliding window algorithm of a fixed size, but we're just going to use a set throughout. So, we're going to start with an empty set. >> [gasps] >> And like we said previously, uh we want the first step of the sliding window of a fixed size algorithm uh to just initialize and process the first k elements. In this particular example, because my anagram has a length of three, really my k or my window size is just three characters. So, if I just grab the first three characters in my first window, I I just add them all uh into my set. So, each character is one item of the set. Great. At this point, I can just check are the characters of my anagram, do they exist within my set? Right now, that's not the case. So, this is not an anagram so far. So, I need to keep going. Like we expect, if you want to do this efficiently, what you want to do is try to keep your operations as constant as possible as you move your window to the right. So, what I'm going to do is I'm just going to remove my trailing character, right? So, I'm going to remove it from my set. And I'm also going to insert my leading character. Going to add that into the set. So, I'll go ahead and remove the G. But then also add the Y. So, now it's as if I'm considering this next substring, R E Y. And again, I check, is my anagram, H O Y, do those characters exist inside of my set? And they don't, so I keep going. I need to progress my window a little bit to the right. So, I remove the R and add the H. That brings me to my next window. Again, I check, are the characters of my anagram, are they present inside of the set? They're not, so I keep going. I remove the E and add the O. Like so. And at this point, I'm considering the substring Y H O. And now I check, does my anagram match the characters in my set? That is actually the case. So, I know I just found a substring that contains the anagram. So, I should just ultimately return true. Obviously, if you uh need to return false, then you're just going to iterate your sliding window all the way to the right. And if you never find a match, then you're just going to return false. Some very classic Boolean logic there. So, let's discuss the complexity of this one. So, if you say that N is the length of my string and K is the length of the anagram I'm looking for, then the time complexity here is going to be O of N times K. And so, where does this come from? If for the sake of sliding the window to the right, we know we're keeping the operations relatively efficient by just removing a character and also adding a character on every iteration as we slide to the right. So, you're probably wondering where does the K come from? What we have to understand is the K comes from the check of checking if our original anagram if it matches the characters of our current set. To do that check, you're just going to really just iterate through the characters and make sure that each one exists. And of course, that will happen in K time cuz there are K characters to verify inside of the set. In a similar way, the space complexity is going to be O of K. Now, really the space complexity is going to be the size of the window cuz we have that many characters inside of our set. This is actually a nice efficient solution for this one. I do note that the complexity for this is a little different from the previous problems that we just did in the sliding window module. Uh mainly, we have a factor of K within the time and space complexity. So, for I think the last three problems, we had a time complexity of O of N and a space complexity that was constant. Here, it's a little different just based on the constraints in the problem. Like you expect, time and space complexity can differ from problem to problem even when it comes to the same topic. And that feels okay because this problem is definitely a step up from the previous problems that just dealt with sums and products previously. Although, they still use the sliding window algorithm. Hey programmers, Alvin here. Right now, let's go over the Python walk-through for this has substring anagram problem. So, like we said in the approach video, we're going to solve this one using our sliding window with a fixed-sized algorithm. What we're going to do is create a set here to contain the characters. That way, we can check if we have a matching anagram here. So, I'll create a set. And I do want to initialize the set with the characters of the very first uh window. I know that the window size is really just going to be the length of the anagram. So, for this particular example, my anagram is H-O-Y. That means I want to check for a sliding window with a the of three here. So, what I'll do is I'll just grab the first three characters or the first K characters, so to speak. So, I'll say S and I'll go from index zero up to the length of the anagram. So, length of anagram. And Python syntax, I could actually drop the zero like so. If you want, you can maybe even declare like a K variable for this. It'll look more similar to code we've written previously. And then, that'll just look like this. So, this is me processing my very first window. Right? And I'll call that my window set. Nice. So, now that I have a set containing these three characters, what I want to do is check if that first window actually is a match. So, be sure to do that. It's very common to forget to do this for the very first window in your sliding window algorithm. So, I'm just going to check if my window set is equal to the set of the original characters of anagram. So, I'll just create another set here. I'll call it my anagram set. Set. This will be the set of the characters of the original anagram. Nice. And so, what I'll do is just check if they match. Python has some really great syntax for this. All you need to do is check if they are double equals. You're going to check if the items in the two sets are the same, right? In other words, are these two sets totally equal? Great. So, if that's the case, then I can return true cuz I just found a match. Otherwise, I don't need to check the other windows by sliding to the right. So, I'm going to set up a for loop. I'll say for I in range go from the very beginning all the way up to but not including my length of S minus the length of the anagram, which I have stored in this like K variable. So, I'll use it over here. Just say K. The stopping condition here is very similar to what we've written in our last few problems. And so here, what I'll do is the very classic sliding window logic. So I'm going to remove from my set I'll say window set dot remove. I'm going to remove my trailing character which would be S at index I. Right? So on the very first iteration when I is zero we're going to remove the G. Then what I need to do is add the Y here. So remove S of I. And then on the other hand you want to add S at I plus K. Right? Cuz if my I is zero then if I do zero plus three I get one, two and then three is the Y right there. Cool. That'll bring me to my next window which I should now check. So I can just check if this window set is equal to the anagram set. If they match then return true. Always having an early return true here. And only after I'm done with the for loop if I never return true then I never found a matching window. So then I can ultimately return false here. Before we give this a run don't forget to return the true value here. Let's test it out. There we have our solution for this one. Like we say in the approach video if you say that N is the length of the string and then K is the length of the anagram this would have an overall time complexity of N times K. It's pretty obvious that the N exists like right about here cuz we're iterating through roughly the length of our string. The K comes from this check over here. Although it's a simple a double equals in Python under the hood this would actually have to iterate uh between one of our sets right and check if every member of that set is within the other. Right? So this is really your K operation inside of your N loop. So that's why you have a total time complexity of N times K. Right? Space complexity here is just going to be K uh based on the size of the sets you create. So mainly your window set and also your anagram set. They're going to have uh the number of characters that is in the original anagram here. Hey programmers, Alvin here. Right now, I want to go over the approach we can use for this count substring anagrams problem. So, in this problem, we're going to be given a string S in input. We're also going to be given another string representing an anagram. What I want to do is count the number of times that the anagram appears inside of the string. Of course, like always, an anagram is a string where we can rearrange the order of the characters. So, if I wanted to actually step through this particular example, my anagram is ATT, and I can actually find that exactly in the string. So, that gives me a count of one. I can also find occurrences where uh the anagram is rearranged, right? So, here I have TTA. Finally, at the end, I have TAT. That would give me a total count of three substrings that match with the given anagram. So, you just want to return the number three here. So, this problem seems pretty typical of what we've been studying lately, especially when compared to the previous problem. There is something important we do want to note in this particular input though. Uh we are allowed to have duplicate characters in the input. And this contrasts with the previous problem where we were guaranteed only unique characters. It's pretty plain to see in my input string here, A appears multiple times, and even the T appears multiple times. And so, let's say we maybe started to attack this similar to our last problem using a set. So, we know in the long run we're going to use, you know, a sliding window strategy. And if you had your initial window here, you would convert the characters into members of a set. So, the corresponding set for your window would look like this. Just like our last problem, nothing fancy. But then, if you actually move this window to the right one unit, you would end up with this. And you could immediately spot the issue here. My window has a size of three, and it contains ATT. If I look at the corresponding set I would have created here, it only contains a single A and a single T. That's really just a constraint of sets, right? In a set, the elements of a set are necessarily unique. You can't have duplicate elements in a set. And so this lack of correspondence is going to cause an issue for us, right? My set only contains two characters, uh but the window I'm looking at has three characters. And so that's why you won't be able to use a set for this one. You do need to adjust this logic a little bit. What you can do to solve this one is just modify the logic and use a map. Like you already know, sets and maps are both hash data structures, so they give us a constant time look up and access. However, a map gives you a key-value pairing. And so what will your key and your value be in this particular problem? Well, your key is just going to correspond to a character of your window. And the corresponding value is just going to be the counts of that character. Because a character can be duplicate and appear multiple times in our window, we're going to use a count to represent that information. So let's step through our sliding window algorithm, but this time using the map here. So we're going to start at the very beginning, and for the most part this is going to feel like a pretty classic sliding window of a fixed-size algorithm, just adjusting for our map logic, right? And so when I create my initial window, of course I need to consume these first three characters, and all that means is I uh put them inside of my map here and give them the appropriate counts, right? So G appears once, A appears once, and T appears once. What you can also do is generate a similar map for your uh input anagram. So my input anagram has one A and two T's. That's going to be useful when you actually compare them during your algorithm. And because in the long run I'm interested in the counts, right? I want to find the number of substrings uh that matches the anagram, I'm going to initialize some count to zero. If I double-check the initial window I have right now, it doesn't match the anagram. So I don't count it, that's why my count begins at zero. And now I can enter the general algorithm. So I'm going to have my trailing element here, which is the G. I also have my leading element, which is the next T over here. Like you expect, you want to just remove your trailing element. So, I've removed the G from my map, which means I reduce its count to zero. At the same time, I need to add my leading element in, so I need to add an additional T. So, the count of T is now two. So, now the map actually represents the next window over here. At this point, I just want to note something important that might factor into the code that you write for this. So, I just decremented the G from a one down to a zero. That essentially means that if G is zero, that there are no G's in my current window. Right? So, I can really ignore the G completely. Maybe you can remove it from your window map. And if you did that, then you would have a perfect equality uh between your window map, as well as the map of your anagram. So, what I'm saying is you could compare the window map to the anagram map, and you see that they're equal. And so, you could increment your count by one. Cool. So, let's continue the algorithm. I need to uh remove my trailing character of A, and also add in my leading character of A, which has an effect of not actually changing the map. But, it does bring me to that next window. Now that I'm at the next window, I should compare my two maps, and they're equal, so I increment my count to two. This point, I slide my window again, being sure to update the map appropriately. Here, I don't have a match between the two maps, so I just continue. Still no match. Still no match. And finally over here, I do have a match. That brings my total count to three. And that's all there is to it. This feels very similar to our previous sliding window of fixed size algorithms, just adjusting it with this uh count hash map logic. So, if we say that N is the length of our string, and K is the length of our anagram, let's talk about the complexity of this one. Similar to last problem, the complexity for the time is going to be n * k. The n comes from the fact that we have to slide this window all the way through our input string. So, that's where the n comes into play. Where does the k come from? Well, the k comes from the fact that you have to actually compare your two maps. You know that the map in the worst case is going to have the same size as the window, right? So, on every iteration or every time you slide the window, you also need to compare the contents of your map. The sliding part is the n, and the comparison of the maps is the k. So, that's why your total time complexity is n * k. In a similar way, the space complexity is just k, coming from the space consumed by the map you create. In the worst case, if you had all unique characters in your window, you would have k different keys in that map. All right, programmers. What I want you to do is give this code a shot on your own. There is some tricky logic you might need to account for when it comes to handling the map. So, if you get stuck, you can find me in the walk-through video. Hey, programmers. Alvin here. Right now, I want to go over the Python walk-through for this count substring anagrams problem. So, uh to solve this one, we do want to use a bit of a counter hash. And to do that really easily in Python, you could just use the built-in collections.Counter, right? So, what I'll go ahead and do is just from collections import counter. And what I want to do is I'm going to convert just the anagram into a counter of its characters. So, that's really straightforward. I'll call this my maybe just anagram counter. I'll just pass in the entire anagram. And beyond that, what I want to do is establish the counter that's going to contain my window. And I'm going to initialize here. I'm going to say my window counter. And do bear in mind that these counter objects, they're going to represent the character counts of those given strings, right? Just like we saw in the approach. When When my initial window, I do want to create a counter, but I just want to grab uh the first window from my input string S. So, I'm going to do a little slice here. I'm going to say S and start at the beginning, go up to the length of the anagram. So, here uh the size of the window or the K is really just the size of the anagram, right? And so, this will give me just the first uh we'll say K characters of the string and I'll put that into my counter. Great. Now, from here, I can actually initialize the count that I'm eventually going to return. So, I'll call it my we'll say num matches. And it could either be initialized as a one or a zero depending on what my initial window looks like, but I know at the very end I'm going to just return the num matches. [snorts] And so, uh like we always say for our sliding window of a fixed-size algorithm, you have to watch out and be sure to check your initial window. So, I'm going to assign num matches as one if the anagram counter is the same as the window counter, right? Otherwise, it's not the same, so I'll initialize it to zero, right? I'm just checking if the uh window has the same count as my anagram, right? In Python, the counter object's really useful uh because you could just check for equality using double equals. All right, from here I enter my general algorithm, so I need to slide my window. So, I'm going to say for we'll say I in range, and I want to go from the beginning of the string, so just zero, and then up to length of S minus the length of anagram. You want to slide all the way to the right as far as you can. What I want to do is remove my uh trailing character. So, if you wanted to, you can split this up into some variables, maybe you can say trailing char is going to be your S at index I, and then, like you expect, your leading char is going to be S index I uh plus [snorts] the length of the anagram, right? Cool. So, you have your trailing char and your leading char. When it comes to your trailing character, what you want to do is just remove it from your count. So, I can say my window counter access the trailing char and just decrements it. And in the same vein, you also want to uh increment your leading character. Cool. Now that you've slid your window to the right, you just want to perform your check here. Check if your newly adjusted window is actually a match with your anagram. And if it is, then you can just increment num matches. Just some basic counting logic here. All right, let's go ahead and give us a test run. Because we're in Python, we really get to benefit from the built-in counter collection, and it's really easy to solve this. Uh do you notice how similar this code is compared to our previous, you know, sliding window algorithms, right? We still have this concept of initializing our very first window. We have to manually check it. Be sure to do that. So, you know, if we have an initial match, we initialize our count to one, otherwise it's zero. Then from there, we just slide our window to the right using like the general part of the algorithm. Like you expect, uh whenever we move to the right, we remove our trailing element from the collection, and we add in our leading element. And then because this is a counting problem, I just want to go ahead and uh verify, uh do I actually have a match for my window to the anagram? If we do, we just increment our global count here. Like we said in the approach video, uh this code has an N times K complexity, right? So, if I kind of break things down here, when we convert the anagram, which is of size K, right? When we convert our anagram into a counter, of course we have to iterate through it under the hood. So, that's really K steps. Uh similar when we also convert uh just the first few characters or the first K characters of our string into a counter, that would also be doing K iterations, right? And then doing this check over here, right? This is a really important aspect. Although we're doing like a equality, it looks like a simple operation, we are comparing two counter objects, right? In Python, it will literally check, you know, every key of the counter if it's corresponding frequency uh matches the other counter, right? And so, in the worst case, I would have K different keys in each of these counters. So, this simple equality check, it's actually not so simple. It does take K steps. I know it's going to be K because the biggest the window counter is going to get is K, and I also know that the anagram counter is exactly K, right? So, I have a bunch of Ks at the start. When it comes to this for loop here, here we're really iterating uh roughly through the length of our string S, right? So, that's really starting to give us an N. Now, if you look at the operations inside of uh this for loop, this is constant time, this is constant time. All these operations are constant time, except for this over here. Just like it was before, because I'm checking for a equality between my counter objects, under the hood, this has to go through each key and uh verify that the frequency is the same. And like we already said, that was K steps. So, this nested operation is K. And just to be clear, I'm going to put it over here. So, it's really NK for this entire for loop. And so, looking at our kind of overall complexity here, we have a K plus a K plus a K plus an NK. Uh obviously, NK is much bigger than any of these other Ks. So, that's why the overall complexity here is N times K. And that was the time complexity. When it comes to the space complexity, we're just looking at O of K from all of these counters. Hey programmers, Alvin here. Right now, I want to go over the approach you can use for the find subarray sum problem. So, in this problem, we're going to be given an array of numbers as input, as well as some target sum. What I want to do is find a subarray that sums to the given target. And so, for this particular input, the answer would come from this subarray over here where I have the two, one, and the seven because 2 + 1 + 7 is 10. And in the long run, you actually want to return the start and end indices of that subarray. So, what I'm saying is if you actually label all the indices of this array, of course your indices start at zero, the starting index is four and the ending index is six. And notice that here those indices bounds for the start and end are inclusive. So, just bear that in mind. So, what this problem is asking is pretty straightforward, but how can we go about solving it? So, lately I've been showing you the sliding window algorithm. We know that generally the sliding window algorithm is really great at considering a subarrays or substrings. And so far in the course, we've always been dealing with a sliding window of a fixed size. But, you actually won't be able to use that exact algorithm here because you really need to find a variable size. Meaning you won't know exactly how big the subarray is that's going to be the final answer, right? In this particular input, it happened to be a subarray of three elements, but there's no constraint here, right? It could have been two elements, it could have been one element, it could have been five elements. And so, you need to consider windows of variable size. Don't worry, though. We'll be able to use a lot of the logic from our previous renditions of sliding window. We just have to adjust for some different things here. And so, for the sliding window with a variable size, you want to start the algorithm in a similar way where you deal with some indices, right? And so, I'm going to have in red my starting index. And in green, I'm going to have my ending index. And at the beginning, you have them point at the very first index. And so, just so we don't forget it, my start is red and my end is green. And because overall you want to consider the sum of a subarray, you're going to have a sum variable. The sum variable is going to represent the total sum of the current window that you're looking at. And so, how does this algorithm run? Well, what you want to do is look at your ending pointer and just add that element into your sum. So, I'm going to add the three into my sum. That means my total sum is three. And so, you're looking at this window here, right? Notice that my start and end are pointing to the same single element. So, that would just be the subarray with one element inside. That element is just three. And at this point, you could also, you know, check your sum. So, I'm going to check is my current sum of three equal to my target? It's not. So, what I can do is just increase the size of my window. So, I'm going to slide the end pointer to the right by just one position. So, now I'm looking over here. Of course, at this point, you should also add that one into your sum. So, I do one plus my current sum of three. That gives me four. And again, you should continue to check. I'm going to check if my current sum of four is equal to my target of 10. It's not. So, I can keep increasing here. So, now I'm considering this element. So, I add that four into my current sum. My current sum is eight. I compare it to the target. Eight is not equal to 10. So, I keep going. At this point, I add a nine into my sum. That gives me 17. I check 17 is surely not equal to my target. So, I need to keep going. However, there's an important thing to consider here. Now that my sum is 17, when I compare that to my target sum of 10, I really overshot it. A really important constraint they give us in this problem is that they tell us that the input array contains non-negative elements. Because there are no negative elements in the array, if I'm to continue to increase the size of my window, that would only make my sum larger and larger. So, it's 17 now. It's only going to get bigger than 17, right? If I look at my next element, I would have to add a two into my sum. And I'm just getting further and further away from the target cuz I've already overshot it. So, here's where we work in the interesting logic. So far, we've only been incrementing uh the position of our ending pointer. But now, it's time to actually increment the position of our starting pointer. So, I want to pay attention to the red pointer now. And I want to actually move this one to the right. That acts as my trailing element. So, when I move this pointer to the right, that would be effectively removing the three out of the sum. I subtracted the three from the 17, that's why my current sum is 14. And I want to do a check here. I'm going to compare my current sum of 14, and I see that it's still larger than my sum of 10. So, I could move this start pointer even one more position, shrinking the size of my window. Of course, I remove the element out of it. So, now my sum is 13. 13 is still bigger than my target sum of 10, so I should still shrink my window by progressing adjust the starting pointer. Because my current sum of nine is less than my target sum, it's okay to actually increase the size of the window. When you increase the size of the window, you just increment your ending pointer. So, you take the two. That means that your current sum is now 11. 11 is too big, right? 11 is bigger than your target of 10, so you need to shrink your window again. Now, I'm just looking at the window of just the single element two. Two is less than my target sum, so I grow my window. I now have three. Three is less than my target sum, so I grow my window. And at this point, I've actually found the target. Right, my current sum is 10, and my target sum is also 10. At this point, you just want to return the indices of the sub-array that you're looking at. In this case, it would be the four and six. So, there are two key steps here. Whenever your current sum is less than the target, you need to increase the size of your window by incrementing your ending pointer. However, if your sum is too big, if your sum is bigger than the target, you need to decrease the size of the window by incrementing the starting pointer. If you do it this way, you'll eventually be able to find the right target sum. And along the way, you're considering uh all of your windows are variable size. The way we implement this is pretty interesting, so I'll save that detail in the code walk-through. That being said, let's talk about the complexity here. If we say that n is the length of the array, the time complexity here is just O of n. And I think that might be a little surprising. A common mistake I see students make when they look at the drawing for this algorithm is they have a feeling that it's going to be an n squared algorithm. I think that's like a reasonable assumption to make, especially because we have, you know, uh two pointers and it looks like that they loop uh through the same array. But let's actually go through what an n squared would look like. So, looking back at my diagram here, I'm going to step through what to me is classic n squared behavior. I'll call it my n squared-ish behavior. And so, I'm going to start both of my pointers just at the beginning, and if I had a very typical like n squared nested loops, it would look like this. What you'll notice is a very key characteristic of n squared-ish behavior is that once you one of your pointers gets to the end of the array, it has to get reset like so. And it keeps progressing. So, that's what very classic n squared-ish behavior looks like. And you get it commonly from your nested loops. However, we don't have that same pattern here. So, I'm going to reset my drawing, and just looking at the pointers, I'm going to show you exactly how our pointers were updated. It looked like this. And at this point in the trace, we would have actually found the correct answer cuz we have our sum of 10. But let's say that we didn't find the answer, and we had a lot more elements in the input array. If I had to keep searching, they might behave something like this. What you'll notice is, compared to my n squared example, both of these pointers here don't actually ever get reset. They both generally just make progress by sliding to the right. So, we actually don't have N-squared here. What you really have is 2 * N. The two comes from the fact that we have two pointers. The N comes from the fact that each pointer needs to make its way, in the worst case, through the entire length of the array. Like you already know, when I have 2N in my big O complexity, I can just drop the constant factor of two, giving me O of N. That's why your time complexity here is just O of N. Very clever stuff. And the space complexity is constant because you already know why. All right, so that's the approach we can use for this one. I think you should go ahead and just start watching the walk-through video for this. It's going to be our first time implementing the sliding window with a variable size algorithm. It's somewhat similar to the fixed-size algorithm, but it has some major differences. The logic gets pretty tricky, so you'll want to see me in the walk-through video. Hey programmers, Alvin here. Right now, I want to go over the Python walk-through for this find subarray sum problem. Like we said in the approach video, we want to solve this one using our sliding window algorithm, but this time we're going to implement it using a variable-size window. So, I'm going to need to set up a few variables. I'm going to first set up some index I can use to represent the start of my window. Just call it start, initialize it to zero. I also need a way to track the sum of my window. So, I'll call it my window sum, also initialize it to zero. From here, what I want to do is set up a for loop that iterates through my ending index. So, I'm going to say for end in range, it's going to start the very beginning and just go through the length of your list here. So, now that I have the ending index of my current window, what I want to do is start accumulating the sum by using the element at this end index. So, what I'm saying is I could do window sum plus equals, just add the element of your list from this ending index. This would be analogous to adding in the leading element into your window, right? Great. But like we said in the approach video, the interesting part about this algorithm is I do need to make my window smaller sometimes, right? Based on the criteria of the problem, right? I want to find a subarray that matches the given target sum. And if ever I have a window sum that's too big, then I know I need to decrease my window size, right? I could remove some elements out of my window. So what that would look like is like this. I could say while my window sum while big. So if it's bigger than my target sum, then I'm too large, so I should make it smaller. To make it smaller, you want to remove elements out of your window. And like all of our previous sliding window problems, when you remove something out of your window, generally you want to remove your trailing element. So that would be the element at the start index. So what I'll do is I can say window sum - = nums at the starting index. So now that you've removed that trailing element uh from your window, you do want to also increase that starting index. Start + = 1. This has the effect of making my window smaller. And do you note that I have uh this code in a while loop. So it's going to run as many times as needed. So uh however long the window sum is bigger than the target sum, I'm going to keep removing elements out of my window at the front. So that means that when this while loop is done, that means that my window sum is no longer greater than the target sum. It might actually be equal to the target sum. So I'm going to check if my window sum is equal to the target. And if that's the case, then I just found the answer, right? Here they tell us to return the start and end indices of the window that has the given target sum. So for you in Python, just return a tuple of the start and end. So if my window sum equals the target, then I go ahead and return the final answer. If not, then I just go to another iteration of this for loop and just progress my ending uh index. Which would mean that I add an additional element into my window, perform the same operation again and again. Great. Let's go ahead and test this out. And there we have it. I will say that although this code is really short, I think it's a little tricky in its design, right? I do want you to really understand how this code uh corresponds to the drawing we made during the approach video, right? So, in particular, how this while loop works, right? It just removes uh elements uh out of my window sum uh if ever my window sum is too large, right? That gives me the effect of being able to have a window of variable size cuz I decrease my size when it gets too big. And like we also said in the approach video, this code's going to have an O(n) runtime where n is the length of the nums list, right? Although there are two loops here, you do not have a n squared uh runtime. And we did a big explanation of that in the approach video, so go back to it if you need. When it comes to the space complexity, it's pretty plain to see here that we only have a constant amount of space just coming from a few variables that we use. All right, programmers, that's all I got for this one. Coming up next, we're going to practice a lot more of these sliding window of a variable size algorithms. Hey, programmers, Alvin here. Right now, I want to go over the approach we can use for this longest subarray sum problem. So, this problem is going to be just a variation of the last one. So, we're given an array of some numbers, and in the problem, they do tell us that the elements of the array are going to be non-negative. We're also given a target sum. What we want to do here is return the length of the longest subarray that sums to the given target. For this particular input, the answer we should return is four, and that comes from this subarray. Notice that the elements in the subarray do indeed sum up to my target sum of 10, and it happens to be the longest subarray possible that adds up to 10. Bearing in mind that we want to return the length of that subarray. All right, so that's what the problem is asking. Uh how can we go about solving this one? Well, we're going to use our sliding window algorithm. We know sliding window is a good fit here because we need to consider all possible sub arrays. And because we don't know the size of the potential sub array, you want to do the sliding window with a variable size, right? And so, let's go over how this works again. It's going to be very similar to our last problem, just giving us another opportunity to practice. So, what I'm going to do is I'm going to track a key variable. I'm going to track the sum of the elements in my current window. We also need to set up some pointers. I'm going to have in red the starting index of my window, and in green I'm going to have the ending index of my window. So, the way this algorithm works is I want to add my ending element, so the element being pointed to by the green arrow, I want to add that into my sum. So, I'm going to add this four into my initial sum, giving me a current sum of four. And I'm considering this very initial window, right? Just containing the elements four. And of course, because you want to look for your target sum here, you would check if your current sum of four is equal to your target sum. It's not, so you can keep running this algorithm, right? So, I just want to increment my ending pointer, adding another element into my window. Now my sum is seven. It's still too small, so I'm going to take another element. Now I take the three. Notice that right now my sum is right on the money, right? My sum of 10 is equal to the target sum of 10. And because in the long run I want to find the largest window that matches up to 10, what I want to do is just save this in some longest variable. So, so far the longest sub array that sums up to the target, that would be a length of three. Notice that I'm going to be saving in longest the length of the sub array. And I can easily figure out how many elements are in my window by just looking at my pointers, right? There are three elements in my window because my start and end pointers are inclusive. Great. At this point I keep running my algorithm, and what you should do is actually still increment your green pointer. So, you're going to grow the size of the window. And when you do this, you'll see that it's the case that actually my sum is too large now, right? I know that I'm looking for a target sum of 10, but I went over to 12, right? Because they tell us in the problem that our array only contains non-negative elements, if I were to increase my window more and more, I would just get farther away from the target sum cuz I would get larger and larger potentially. So, at this point, I'm too large, so I should decrease my window size. To decrease your window size, you just want to increment your red pointer or your starting pointer. And when you do that, you would remove that element of four from your sum. Great. Now, it's the case that my current sum is okay, right? It's now below 10, so I can keep incrementing it if I need. Whenever your window sum is less than your target, you can just increase the size of your window taking a new element. It's still the case that I'm below the target sum, so I'm going to grow the size of my window. But at this point, I go over to 14, right? So, now I'm too large. So, now I need to decrease the size of my window. I'm down to 11, but it's still the case that I'm still over the target sum, so I decrease again. That brings me to a window sum of eight. At this point, I'm good to go, but I still need to find my target sum of 10, so I keep increasing the size of my window by progressing that ending pointer. At this point, I'm right on the money. My current window sum is 10, my target is also 10, and if I actually look at the number of elements in my window, I have four elements in my window. That's actually more elements than the current longest I have stored. So, now four is my new longest, and I continue my algorithm. I still want to increment my ending pointer, growing the size of my window. This brings me up to 13, which of course is too large, so now I decrease the size of my window. I'm still too large, so I do it again. So, here I hit my target sum, but if I look at the number of elements in my window, I currently have a window size of three, but the longest I've seen is four, so I don't do any replacing here, right? Cuz this three is smaller than that four over there. So, I keep running. So, I increase my window size, but now I'm too large at 15, so I need to decrease it. Here I'm at 10, and again, it's another case where I have three elements in my window, but my longest is four, so I just continue on. I don't do any replacing here. I take my next element, I go all the way up to 20, which of course is too large, so I decrement my window size by progressing that starting pointer. And I need to do that a few times until I get down below my threshold. At this point, I actually hit my target sum of 10, but of course, my window size is only one, and it doesn't get to replace the four. So, I continue my algorithm. I grow my window, bring me up to 11. So, now I need to make my window smaller. If I try to continue the algorithm further, I actually hit the end of my input array. So, I know that I've exhausted uh all possible sub-arrays uh to look at. And so, I can just return what I have stored in longest, which is the final answer of four. So, that's all there is to this algorithm. Uh very similar to our previous problem. If you say that n is the length of the array, the time complexity here is just O of n. In the previous problem, we did a long uh trace through for why the complexity is O of n and not something like an n squared. The short story is that in this problem, we need to actually progress uh both pointers all the way through the array. So, it's going to take n steps to move the red starting pointer through the array, and it's going to take another n steps to move the green ending pointer through the array. So, that's two n iterations overall, which just simplifies to O of n. The space complexity here remains constant cuz we're just using a handful of variables for our window sum as well as the longest length we've seen thus far. All right, programmers, I want you to try to code this one up on your own. You're going to see that the code is almost identical to our previous problem just with a little spin. That way you get to practice this concept. If you get stuck, you can find me in the walk-through video. Hey programmers, Alvin here. Right now, let's go over the Python walk-through for the longest subarray sum problem. So, this is going to be just a spin-off of our last problem, and we're going to use that sliding window with a variable size algorithm. So, we're going to begin with our starting index at zero. I do need to track the longest window over time, so I'm going to uh set my longest equal to zero. I also need to track the sum of my current window, so I'll set my window sum equal to zero. From here, I want to iterate through my end of my window. So, I can say for end in range from zero, go up to the length of the nums. And what you want to do is uh take the elements at these ending indices and add them into your window. So, that'll be my window sum plus equals nums at end. Great. From here, what you want to do is uh you do need to correct your sum if it gets too large. In other words, if my window sum is too big, if it's bigger than the target, then I know I need to bring it back down by reducing the size of my window. So, you can do that by progressing the starting index. But, be sure to also remove the corresponding elements out. So, I can say window sum minus equals nums at start. Great. Now, from here, you just want to go do your uh max value logic. So, I'm going to set the longest equal to the maximum between the length of my current window. The length of your current window would just be end minus start plus one. And also, compare that against what you currently have in longest. Great. Now, after your for loop is done running, just return the longest. This code has a very similar shape to our last problem. You might be wondering about this trick here. So, this just gives us the length of the current window, and we've seen this arithmetic before, but I'll review it over here. Let's say we had just some abstract elements here. E F. I'll label their indices, so 0 1 2 3 4 and 5. And let's say I just had um some particular values for my end and start. Let's say my end is maybe the E element. And then my start, we'll say my start is the B element here. Great. So, I want this quantity to be the number of elements in the window. Do bear in mind that my start and end indices are inclusive, right? So, if I do end minus start, I'll do 4 minus 1, that gives me 3. Then I do this additional plus 1, and that gives me 4, cuz there are four elements in this window. One thing I am forgetting here is be sure to actually uh check if your window sum uh reaches your target sum before you actually do the max value logic. So, you want to write an if statement here. You want to check if your window sum is equal to the target sum, then you can do this stuff. Great. Let's give it a test run. There is another bug we need to fix, but we can fix it together. So, here we're passing the first test case, but we're failing the second one. Second one is a pretty obvious edge case. Um here, this is a circumstance where the target sum is actually not found within the input list here, right? There is no uh subarray that sums up to five. In that circumstance, they want us to return -1. Looks like my code returns zero, right? And that has to do with the default value I chose for the longest, right? And so, what I want to do is actually initialize this longest to be -1. Because if I never find a window that matches my target sum, then you're just going to return the longest variable, which it still contain -1. So, let's give that a test run. Great. And there we have it. So, like we said in the approach video, the time complexity for this is just O of N, cuz we're moving the start index all the way through the list once, and we're also moving our ending index all the way through the list once, right? So, although we have nested loops here, we don't have n squared. And we kind of talked about why that was in the previous problems. The space complexity here just remains constant cuz we're only dealing with a handful of variables. All right, programmers. I hope you're starting to get the hang of the sliding window with a variable size algorithm. Of course, we'll do some variations coming up next. Hey programmers. Now that we've done a few sliding window with variable size problems, I want to go over the recipe for this one. As always, sliding window algorithms are a good choice whenever you need to consider sub-arrays or sub-strings. If your sub-arrays or sub-strings have any size, then you want to use the variable size version, as opposed to the fixed size, of course. So, let's go over the general steps for this. So, for the most part, you typically want to begin by initializing your start pointer to the very beginning of your string or array. So, that would be to index zero. And then from there, you also want to initialize another key variable. You want to represent the window collection. And this might vary from problem to problem. So, as of late, our collection has just been a sum that we add into. But, of course, your collection can really be anything, right? We've seen for our sliding window with fixed size algorithms, sometimes our collection might be a set or it might be a hash map. So, this really depends on the context of the problem. But, typically, you're going to have some collection that represents the aggregate of the window. From there, we iterate the end pointer through the array or the string. And typically, we accomplish this with a for loop. There are a few different steps that happen within this for loop. One thing you'll always want to do is make sure you add the leading element to the collection. Right? So, you want to add the element at the end pointer into the collection. What you also want to do is have a way to remove trailing elements from the collection. And this is a really important step. And what you'll want to notice is when you remove trailing elements from the collection, you might have to do that multiple times cuz you want to continually remove trailing elements again and again while your constraint is violated. And as you remove elements, hopefully, eventually, your constraint is met once again and you're no longer in violation. So, in the context of our previous problems with our sum, whenever our sum got too large, right? Then we started to remove trailing elements from our sum to get back down within that target sum. Like I have highlighted here, you typically do this removing of elements using a while loop, right? This happens many times until your constraint is met once again. So, these two steps of A and B give us our variable-size window. Because step A of adding the leading element to the collection, that would increase the size of our window. Whereas step B of removing a trailing element from the collection, that would decrease the size of our window. So, we're able to grow our window or reduce our window as needed. And finally, the fourth step is to just do the business logic that's specific to the problem you're trying to solve. And this could be a few different things, right? You might want to count something, might want to determine the max or min of something, or just find if something exists. It really depends on the specifics of the problem. And of course, I'm going to show you a few different variations of that coming up next. Before we hop into some more problems, I just really want to emphasize the importance of step B. To me, this is what really makes the sliding window with a variable-size algorithm, it makes it special, right? It's how we remove trailing elements from the collection while our constraint is violated. The important part of these algorithms is really determining when we violate our constraint and how we can actually undo things to get back within that constraint. All right, so that wraps up my recipe for the sliding window with variable size. Do you bear in mind then that this is just a broad guideline, right? You might not exactly be able to implement these steps to a T to solve every sliding window with variable size. Sometimes you'll have to adjust things. But as we do more problems together, I think you'll get a good sense of that. All right, let's get to it. Hey programmers, Alvin here. Right now, let's go over the approach we can use for this longest unique substring problem. So, in this problem, we're going to be given just a string S as input. And what we want to do is return the length of the longest substring that contains only unique characters. So, here the result we should return is four. And that comes from this corresponding substring. This is the longest substring with no duplicated characters. To solve this one, what you want to do is use your sliding window algorithm. Because we want to consider different substrings, and we also don't know how long the substrings need to be, we're going to use a sliding window with a variable size, of course. And so, like you always expect, we're going to set up our pointers. In red, I'm going to have my starting index for my window. And in green, I'm going to have my ending index for the window. And because I want to ultimately return the length of the longest substring, I'm going to have a variable to track the longest. You could just initialize it to zero, right? Because you haven't found any substring yet. In order to track what characters you've seen thus far, I think it's great if you represent the characters that are currently in your window using some sort of counter hash map. In other words, over time the keys of this hash map are going to be characters. The corresponding values are going to be the counts of those characters within my window. Similar to some previous problems. So, to start this algorithm, what I do is I take the current element at my ending pointer, and I add it into my counter data, my window data. So, I'd be looking at this initial window. So, I take the A and just add it into my counter. So, that means A is a key, and now its count is one. At this point, to be complete, we should actually do our kind of like max value logic. Trivially, A is indeed a substring that contains only unique characters, and its length is one. So, one could actually replace this longest of zero. So, to continue the algorithm, I go ahead and increase my window size. So, now I take the B. If I look at my window data, it's still the case that my window contains only unique characters. I know they're only characters because all of the counts are one. Because I have a current window size of two, I actually replace that into my longest. And I keep running. Increase my window size again by progressing the ending pointer. And it's still the case that I have unique characters here. Of course, my window size is three, so that becomes my new longest thus far. On the next iteration, I still try to increase my window size taking a new element. Uh but at this point, I see that I no longer have unique elements within my window because A hit a count of two. If any of your character counts are more than one, then you definitely have a duplicate character inside. And like we expect, now that we've violated our window constraint, what we want to do is now decrease the size of our window until we're back within the constraint. And so, what I do is I increment my ending pointer, uh really making my window smaller and removing an element from the window. Just like that. And as soon as I do that, I actually bring my window data back within the constraint. It looks like everything here is indeed unique. The window size is three, and it's not longer than the longest, so I don't need to replace it here. At this point, I can increase my window size, but as soon as I do that, I violate the constraint, right? Cuz B has a count [clears throat] of two. So, what I want to do is now decrease my window size till I'm within constraint. Now that I'm good, I can increase my window size, but of course, as soon as I do that, looks like C violates my unique constraint. So, I decrease my window size. I'm back within constraint now. At this point, I increase my window size. This actually gives me a new character of Q, and I'm actually still within constraint because all of my character counts are less than two. Notice that the length of my window now is four, and four is bigger than three, so I should take the four as my current longest. At this point, I can keep increasing my window size, but this actually violates the uniqueness of B. And so, I decrease my window size, and this is actually a really important scenario. Uh notice that although I decrease my window size, I still am violating my constraint cuz I I didn't actually change the counts of B. What I did was actually remove an A out of my window data. That comes from down here, right? So, when you actually go ahead and try to make your window smaller, when you're trying to get back within the constraints of your window, uh could be the case that you might need a few iterations to get within that constraint. And so, on this next iteration, I do have to make my window smaller again. And this is actually where I correct things, right? I get rid of that B, and that's good because I'm no longer violating my uniqueness constraint. At this point, everything looks unique and good to go, so I can go ahead and make my window bigger. At this point, I violate B once more, so I need to make my window smaller. I get rid of the C, still violate, get rid of the Q, still violate, and I get rid of the B. Now, I'm actually back within spec. Obviously, at this point, my window is very small. Although I have a unique substring, it only has a size of one, so it surely doesn't get to replace my longest. At this point, if you try to actually increase the size of your window, you actually finished all of your iterations. And so, you can just return the value you have stored in longest. So, your final answer is four. So, like we expect here, if we say that N is the length of our input string, the time complexity is O of N, similar to our previous sliding window of variable size algorithms. Although we have to slide both of our pointers through the full length of the string, that really gives us a 2N, right? The first N is for me sliding the red pointer all the way through the string, and the second N comes from me sliding the green pointer all the way through the string, right? We don't get an N squared here, like we always say. Here, the space complexity is also going to be linear, coming from the counter hash map that we maintain. In the worst case, you might have a whole bunch of different characters within your counter hash map. So, overall, you have linear time and space for this one. Hey programmers, Alvin here. Right now, let's go over the Python walk-through for this longest unique substring problem. So, as always, like we've been doing as of late, we're going to solve this one using our sliding window with a variable size. And this time we're dealing with some some strings, right? So, I want to mess with some character counts. So, since you're doing this in Python, you have the luxury of maybe using the built-in counter collection. So, I'll say from collections import Counter. And I'll initialize one down below. I'll call it my, I don't know, window counter. And what are some other variables that we're going to need here? Well, typical for most of your sliding window variable size algorithms, you're going to need the start index. And because here I'm ultimately concerned with returning the length of the longest unique substring, going to have a longest variable. I'll initialize that longest length to be zero. Of course, I'm going to replace it over time using some max value logic. So, from here, I want to iterate through all of the end indexes. So, I'll say for end in range, go from zero up to the length of the string. Cool. And what I want to do is I'll go ahead and grab um the character at this index. So, I can say S at end. And cuz I'm going to need to reference it a few times, this might be a little cleaner to have a variable. I'll call it my leading character. Some familiar naming for us. Great. The nice part of using the counter collection in Python is that you can just go ahead and start by incrementing your characters because if a key does not yet exist inside of your counter, it implicitly has a count of zero. So, what I'm saying is I can take window counter of leading char and just increment it immediately, right? This is safe to do. Uh even if the um leading character does not exist explicitly as a key in the counter, it implicitly has a key, right? It's corresponding value is zero, so now I increment it if it's the first time. Great. So, that gives me a way to add elements or characters into my window. Now, I need a way to remove them. So, when should you remove characters here? Because in the long run, you want to find the longest unique substring, you want to remove characters while you're non-unique, right? If ever you violated your constraint, you're going to keep removing stuff until you're back within that constraint. So, I want to consider the character I just added, and I want to uh make sure that it's unique. So, I'll actually just grab this here. So, if it's non-unique, so if its count is bigger than one, then I need to keep removing stuff, right? So, I need to remove a character here. You always want to remove from the uh back of your window. So, that'll be S start. And I'm going to use some familiar names here. I'll call this my trailing char. And so, first I'll decrement the count in the window counter. So, window counter of trailing char -= 1. And because you're making your window smaller, be sure to increment the start, right? Start += 1. Great. So, this while loop is going to remove as many characters as I need until I'm guaranteed that uh my substring is now unique. So, now that I have a unique substring, I want to do the max value logic. So, we'll just set longest equal to the max between the current window length, which is end minus start plus one, and then compare that against the longest you have stored thus far. And you've seen kind of the math behind this a few times by now. After your for loop is done running, just return whatever you have stored in longest. Great. So, very typical structure here, right? We have our while loop inside of a for loop. The while loop has the job of uh bringing us back within the constraints of the problem, right? To make sure we have a unique substring here. So, let's test it out. Great. Looks like this is passing. So, uh a few ways you can kind of write this. Um I kind of wrote this for loop so it iterates through the the kind of index numbers directly. If you want, you can do your fancy Python thing, so I'll kind of leave that to you, but you know, you could do stuff like enumerate. Enumerate. If you enumerate S, you'll have access to the ending index, right? And you also have access to the element itself, in this case the leading char. So, you kind of skip this line here. That would be functionally the same, a little more Pythonic as well. If you remember to use the colon. Great, still passes. Like we said in the approach video, this code has an O of N run time, like our previous problems, right? The only thing that's unique about it now is actually also has an O of N space complexity, right? Of course, coming from the counter we create. In the worst case, we're going to have a key for every character in from our input string here. All right, programmers, that wraps up our course on the sliding window algorithm. We solved a bunch of problems together. We started out with some basic fixed-size window problems, and then explored some advanced variable-sized ones. This course might be over, but the learning doesn't stop here. If you enjoy my teaching style and want to practice more sliding window problems, then you should check out my full algorithms course at structy.net. There, I'll teach you everything you need to know to pass your DSA interviews. I'll leave a link in the video description, if you're interested. That's all for now. Thanks for watching.

Generated algorithmically for Search Engine Indexing.

Summarize Another Video