276. Paint Fence 🔒
Problem Description
You need to paint a fence that has n
posts using k
different colors. Each post must be painted with exactly one color, and you cannot have three or more consecutive posts painted with the same color.
For example, if you have 3 posts and 2 colors (say red and blue), some valid ways to paint would be:
- red, blue, red
- red, blue, blue
- blue, red, red
- blue, red, blue
But painting all three posts the same color (red, red, red or blue, blue, blue) would be invalid since that creates three consecutive posts with the same color.
The problem asks you to find the total number of different ways you can paint all n
posts while following these rules.
The solution uses dynamic programming with two arrays:
f[i]
tracks the number of ways to paint posts0
toi
where the last two posts have different colorsg[i]
tracks the number of ways to paint posts0
toi
where the last two posts have the same color
The key insight is that:
- If the last two posts have different colors (
f[i]
), the current post can be any of thek-1
colors different from the previous post - If the last two posts have the same color (
g[i]
), the current post must be different from the previous two, so we transition from thef[i-1]
state
The final answer combines both possibilities: f[n-1] + g[n-1]
.
Intuition
When thinking about this problem, we need to keep track of whether we can paint the current post with the same color as the previous one or not. This depends on the pattern of the last two posts.
The key observation is that at any post i
, we only care about two scenarios:
- The previous two posts have different colors - in this case, we can paint the current post either the same as the last post or different from it
- The previous two posts have the same color - in this case, we must paint the current post with a different color to avoid three consecutive posts with the same color
This naturally leads us to track two states:
f[i]
: ways to paint up to posti
where postsi-1
andi
have different colorsg[i]
: ways to paint up to posti
where postsi-1
andi
have the same color
For the transitions:
-
To achieve different colors at positions
i-1
andi
(statef[i]
), we can come from either state at positioni-1
. If we had different colors ati-2
andi-1
, we havek-1
color choices for posti
. If we had same colors ati-2
andi-1
, we also havek-1
choices (anything except the color used for the last two posts). Sof[i] = (f[i-1] + g[i-1]) * (k-1)
-
To achieve same colors at positions
i-1
andi
(stateg[i]
), we can only come from the state wherei-2
andi-1
had different colors, and we paint posti
the same as posti-1
. Sog[i] = f[i-1]
Starting with the first post, we have k
ways to paint it (which we consider as "different" from a non-existent previous post), so f[0] = k
and g[0] = 0
.
Solution Approach
We implement the solution using dynamic programming with two arrays to track the painting states.
State Definition:
f[i]
: number of ways to paint posts[0..i]
where the last two posts have different colorsg[i]
: number of ways to paint posts[0..i]
where the last two posts have the same color
Initialization:
f[0] = k
: The first post can be painted with any of thek
colorsg[0] = 0
: There's no previous post to have the same color with
State Transition:
For each post i
from 1
to n-1
:
-
Calculate
f[i]
(last two posts have different colors):- We can arrive at this state from either
f[i-1]
org[i-1]
- From both previous states, we have
k-1
color choices (any color except the one used for posti-1
) - Formula:
f[i] = (f[i-1] + g[i-1]) * (k-1)
- We can arrive at this state from either
-
Calculate
g[i]
(last two posts have the same color):- We can only arrive at this state from
f[i-1]
(previous two were different) - We paint post
i
with the same color as posti-1
- Formula:
g[i] = f[i-1]
- We can only arrive at this state from
Final Answer:
The total number of ways is f[n-1] + g[n-1]
, which accounts for all valid painting configurations where the last two posts either have different colors or the same color.
Time Complexity: O(n)
- we iterate through all posts once
Space Complexity: O(n)
- we use two arrays of size n
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with n = 3
posts and k = 2
colors (let's call them Red and Blue).
Step 1: Initialize for post 0
f[0] = 2
: We can paint the first post either Red or Blueg[0] = 0
: No previous post exists to have the same color with
Step 2: Calculate for post 1
f[1] = (f[0] + g[0]) * (k-1) = (2 + 0) * 1 = 2
- These represent: (Red, Blue) and (Blue, Red)
g[1] = f[0] = 2
- These represent: (Red, Red) and (Blue, Blue)
Step 3: Calculate for post 2
f[2] = (f[1] + g[1]) * (k-1) = (2 + 2) * 1 = 4
- From (Red, Blue): we can paint Blue → (Red, Blue, Red)
- From (Blue, Red): we can paint Red → (Blue, Red, Blue)
- From (Red, Red): we must paint Blue → (Red, Red, Blue)
- From (Blue, Blue): we must paint Red → (Blue, Blue, Red)
g[2] = f[1] = 2
- From (Red, Blue): we can paint Blue → (Red, Blue, Blue)
- From (Blue, Red): we can paint Red → (Blue, Red, Red)
Final Answer: f[2] + g[2] = 4 + 2 = 6
The 6 valid ways to paint 3 posts with 2 colors are:
- Red, Blue, Red
- Blue, Red, Blue
- Red, Red, Blue
- Blue, Blue, Red
- Red, Blue, Blue
- Blue, Red, Red
Notice how we avoid having three consecutive posts with the same color (Red, Red, Red or Blue, Blue, Blue would be invalid).
Solution Implementation
1class Solution:
2 def numWays(self, n: int, k: int) -> int:
3 # Edge case: no posts to paint
4 if n == 0:
5 return 0
6
7 # Edge case: only one post
8 if n == 1:
9 return k
10
11 # diff[i]: number of ways to paint posts 0...i where post i has different color than post i-1
12 # same[i]: number of ways to paint posts 0...i where post i has same color as post i-1
13 diff = [0] * n
14 same = [0] * n
15
16 # Base case: first post can be painted in k ways
17 diff[0] = k
18 same[0] = 0 # No previous post to be same as
19
20 # Fill the dp arrays
21 for i in range(1, n):
22 # Current post has different color than previous post
23 # Can come from either previous diff or same state, and choose from k-1 colors
24 diff[i] = (diff[i - 1] + same[i - 1]) * (k - 1)
25
26 # Current post has same color as previous post
27 # Can only happen if previous two posts had different colors
28 same[i] = diff[i - 1]
29
30 # Total ways is sum of both possibilities for the last post
31 return diff[n - 1] + same[n - 1]
32
1class Solution {
2 public int numWays(int n, int k) {
3 // dp[i] represents number of ways to paint posts 0 to i
4 // where last two posts have different colors
5 int[] differentColors = new int[n];
6
7 // dp[i] represents number of ways to paint posts 0 to i
8 // where last two posts have the same color
9 int[] sameColors = new int[n];
10
11 // Base case: first post can be painted in k ways
12 // (all colors are considered "different" from non-existent previous post)
13 differentColors[0] = k;
14
15 // Dynamic programming iteration for remaining posts
16 for (int i = 1; i < n; ++i) {
17 // Current post has different color from previous post
18 // Can choose from (k-1) colors, previous state can be either same or different
19 differentColors[i] = (differentColors[i - 1] + sameColors[i - 1]) * (k - 1);
20
21 // Current post has same color as previous post
22 // Only valid if previous two posts had different colors
23 sameColors[i] = differentColors[i - 1];
24 }
25
26 // Total ways is sum of both ending states
27 return differentColors[n - 1] + sameColors[n - 1];
28 }
29}
30
1class Solution {
2public:
3 int numWays(int n, int k) {
4 // Edge case: no posts to paint
5 if (n == 0) return 0;
6
7 // dp_diff[i]: number of ways to paint posts 0..i where post i has different color than post i-1
8 // dp_same[i]: number of ways to paint posts 0..i where post i has same color as post i-1
9 vector<int> dp_diff(n);
10 vector<int> dp_same(n);
11
12 // Base case: first post can be painted in k ways (all different from "nothing")
13 dp_diff[0] = k;
14 dp_same[0] = 0; // No previous post to be same as
15
16 // Fill the dp arrays for remaining posts
17 for (int i = 1; i < n; ++i) {
18 // Current post has different color than previous post
19 // Can come from either previous configuration (same or different)
20 // and choose from (k-1) colors different from previous post
21 dp_diff[i] = (dp_diff[i - 1] + dp_same[i - 1]) * (k - 1);
22
23 // Current post has same color as previous post
24 // Can only happen if previous two posts had different colors
25 // (to avoid three consecutive posts with same color)
26 dp_same[i] = dp_diff[i - 1];
27 }
28
29 // Total ways is sum of both configurations for the last post
30 return dp_diff[n - 1] + dp_same[n - 1];
31 }
32};
33
1/**
2 * Calculate the number of ways to paint n fence posts with k colors
3 * where no more than two adjacent posts have the same color
4 * @param n - number of fence posts
5 * @param k - number of available colors
6 * @returns total number of valid painting combinations
7 */
8function numWays(n: number, k: number): number {
9 // Arrays to track painting possibilities
10 // differentColorCount[i]: ways to paint post i with different color than post i-1
11 // sameColorCount[i]: ways to paint post i with same color as post i-1
12 const differentColorCount: number[] = Array(n).fill(0);
13 const sameColorCount: number[] = Array(n).fill(0);
14
15 // Base case: first post can be painted with any of k colors
16 differentColorCount[0] = k;
17
18 // Calculate possibilities for each subsequent post
19 for (let i = 1; i < n; ++i) {
20 // To paint current post different from previous:
21 // - If previous two were different, we have k-1 choices
22 // - If previous two were same, we also have k-1 choices
23 differentColorCount[i] = (differentColorCount[i - 1] + sameColorCount[i - 1]) * (k - 1);
24
25 // To paint current post same as previous:
26 // - Only possible if previous two were different (to avoid 3 consecutive same colors)
27 sameColorCount[i] = differentColorCount[i - 1];
28 }
29
30 // Total ways is sum of both possibilities for the last post
31 return differentColorCount[n - 1] + sameColorCount[n - 1];
32}
33
Time and Space Complexity
The time complexity is O(n)
where n
is the number of fence posts. The algorithm uses a single for loop that iterates from index 1 to n-1
, performing constant time operations in each iteration (arithmetic operations and array assignments).
The space complexity is O(n)
where n
is the number of fence posts. The algorithm creates two arrays f
and g
, each of size n
, to store intermediate results. Therefore, the total space used is 2n
, which simplifies to O(n)
.
Common Pitfalls
1. Incorrect State Definition Leading to Triple Consecutive Colors
A common mistake is misunderstanding what the states represent, particularly thinking that same[i]
means "all posts up to i have the same color." This would violate the constraint of not having three consecutive posts with the same color.
Wrong interpretation:
# INCORRECT: This doesn't prevent three consecutive same colors same[i] = same[i-1] # Wrong! This would allow unlimited consecutive same colors
Correct interpretation:
The state same[i]
specifically means posts i-1
and i
have the same color (not all posts). The transition same[i] = diff[i-1]
ensures we can only have at most two consecutive posts with the same color, as we're coming from a state where the previous two were different.
2. Space Optimization Confusion
When optimizing space from O(n) to O(1), a common pitfall is incorrectly updating variables in the wrong order:
Incorrect space-optimized version:
def numWays(self, n: int, k: int) -> int:
if n == 0: return 0
if n == 1: return k
diff = k
same = 0
for i in range(1, n):
diff = (diff + same) * (k - 1) # Wrong! diff is modified before being used for same
same = diff # This uses the NEW diff value, not the previous one
return diff + same
Correct space-optimized version:
def numWays(self, n: int, k: int) -> int:
if n == 0: return 0
if n == 1: return k
diff = k
same = 0
for i in range(1, n):
prev_diff = diff # Store the previous value
diff = (diff + same) * (k - 1)
same = prev_diff # Use the stored previous value
return diff + same
3. Missing Edge Cases
Forgetting to handle edge cases when n = 0
or n = 1
can cause array index errors or incorrect results:
Problem scenario:
# Without edge case handling diff[1] = (diff[0] + same[0]) * (k - 1) # Crashes when n = 1 (index out of bounds)
Solution:
Always check for n = 0
and n = 1
cases before entering the main loop, as these don't follow the general recurrence relation.
4. Overflow in Large Inputs
For very large values of n
and k
, the result can exceed integer limits. While Python handles big integers automatically, in other languages you might need modulo arithmetic:
For languages with integer overflow:
MOD = 10**9 + 7 # Common modulo value in competitive programming diff[i] = ((diff[i-1] + same[i-1]) * (k-1)) % MOD same[i] = diff[i-1] % MOD
How would you design a stack which has a function min
that returns the minimum element in the stack, in addition to push
and pop
? All push
, pop
, min
should have running time O(1)
.
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!