Note: character position in my explanation begins at 1, so the first character is at position 1, the second character at position 2 and so on.
Let's define:
valid string: string formed using k characters such that at most two adjacent characters can be the same.
f(n): number of valid string of length n and the last two characters are different.
g(n): number of valid string of length n and the last two characters are the same.
h(n): number of valid string of length n.
Assume you have already calculated the value of f(m) and g(m) for all m <= n: let's find f(n+1) and g(n+1).
Let's calculate f(n+1): number of valid string of length n+1 and the last two characters are different. Based on every valid string of length n you have found:
if last two characters are different, you have k-1 ways to place a character at position n+1 and form a valid string of length n+1 where last two characters are different.
if last two characters are the same, you have k-1 ways to place a character at position n+1 and form a valid string of length n+1 where last two characters are different.
These two options are clearly mutually excluse because last two characters of every valid string of length n can't be both equals and different. Also, these two options are enough to help us to correctly calculate all valid string of length n+1 where last two characters are different, because every valid string of length n+1 where last two characters are different has the characters at position n-1 and n either different or the same (both options above).
So we can finally say: f(n+1) = (k-1) * f(n) + (k-1) * g(n)
| first option | |second option|
Let's calculate g(n+1): number of valid string of length n+1 and the last two characters are the same. Based on every valid string of length n you have found:
if last two characters are different, you have just 1 way to place a character at position n+1 and form a valid string of length n+1 where last two characters are the same.
if last two characters are the same, you have 0 ways to place a character at position n+1 and form a valid string of length n+1 where last two characters are the same, because a valid string has at most two equals adjacent characters. So if characters at position n+1 and n are the same then last 3 characters are the same.
Once again, these two options are clearly mutually excluse because last two characters of every valid string of length n can't be both equals and different. Also, these two options are enough to help us to correctly calculate all valid string of length n+1 where last two characters are the same because every valid string of length n+1 where last two characters are the same has the characters at position n-1 and n either different or the same (both options above).
So we can finally say: g(n+1) = f(n)
| first option |
Now it's straightforward to see that: h(n) = f(n) + g(n) because last two characters of a valid string of length n are either equals or different.
Given that equation and replacing g(n) with it's value, we have: h(n) = f(n) + f(n-1).
Also, given both f(n) and g(n) just need 1 previous value to be correctly calculated, we have to calculate f(1) and `g(1)`` beforehand.
It's easy to see that:
End of mathematical part. Begin of programming part :)
Note: all solutions are written in python code.
You have 3 ways to do this, both of them calculate h(n) with O(n) time complexity and the third calculates h(n) with O(log n) time complexity. Let's check them!
Solution #1: Recursive
Time Complexity: O(n)
For solution #1 let's ask for values and assume previous values are already calculated (for this we need recursion).
def f(n, k):
return k if n == 1 else (k - 1) * (f(n - 1, k) + g(n - 1, k))
def g(n, k):
return 0 if n == 1 else f(n - 1, k)
def h(n, k):
return f(n, k) + g(n, k)
Solution #2: Iterative
Time Complexity: O(n)
For solution #2 let's calculate the next value of those formulas based on previous already calculated values.
def h(n, k):
fn, gn = k, 0
for _ in range(n):
hn = fn + gn
fn, gn = (k - 1) * (fn + gn), fn
return hn
Solution #3: Matrix Exponentiation and Binary Exponentiation
Time Complexity: O(log n)
Given h(n) = f(n) + g(n) and g(n) = f(n-1) then h(n) = f(n) + f(n-1).
Also, given f(n) = (k-1) * f(n-1) + (k-1) * g(n-1) and g(n) = f(n-1)
then f(n) = (k-1) * f(n-1) + (k-1) * f(n-2).
So with matrix and vectors below and assuming f(0) = 0, we can correctly calculate h(n) as follows:
| k - 1 k - 1 |^(n-1) * | f(n-1) | = | f(n) |
| 1 0 | | f(n-2) | | f(n-1) |
Code:
def h(n, k):
def _matrix_multiplication(matrix_a, matrix_b):
result = [ [ 0 ] * len(matrix_b[ 0 ]) for _ in range(len(matrix_a)) ]
for i in range(len(result)):
for j in range(len(result[ 0 ])):
for k in range(len(matrix_a[ 0 ])):
result[ i ][ j ] += matrix_a[ i ][ k ] * matrix_b[ k ][ j ]
return result
def _binary_exponentiation(matrix, n):
result = [
[ 1, 0 ],
[ 0, 1 ]
]
while n != 0:
if n % 2 == 1:
result = _matrix_multiplication(result, matrix)
n //= 2
matrix = _matrix_multiplication(matrix, matrix)
return result
matrix = [
[ k - 1, k - 1 ],
[ 1, 0 ]
]
matrix = _binary_exponentiation(matrix, n - 1)
vector1 = [ [ k ], [ 0 ] ]
vector2 = _matrix_multiplication(matrix, vector1)
return sum(row[ 0 ] for row in vector2)
I hope any of these 3 solutions help you!