The hints for this problem suggest approaching it as a linear algebra problem. The idea is that we will have a vector representing the counts of each letter in s. When we do a transform, we want to find out what the counts will be in the new vector. We can represent by creating a matrix that represents the shift. Each row in the matrix will have a 1 for each letter added after the transfrom and 0 otherwise. Then when we multiply the vector against this matrix the product will have the counts of all the letters after a transform. We can repeat multiplying by the transform matrix every time we need to perform a transformation.
Since we multiplying the same matrix by itself over and over again, we can optimize this by doing repeated squaring. Instead of multiplying by X t times, we can instead do X*X to get X^2, then X^2 * X^2 to get X^4, then X^4 * X^4 to get get X^8. This way we're doubling the number of X's with each square. If it isn't an exact power of 2, then we can recursively do repeated squaring on the remaining multiplications. I've memorized the squares as we go to try to save some time on this recursion as well.
def matrixMultiply(a, b):
rows = len(a)
n = len(a[0])
cols = len(b[0])
result = [[0 for _ in range(cols)] for _ in range(rows)]
for m in range(rows):
for p in range(cols):
for ind in range(n):
result[m][p] += (a[m][ind] * b[ind][p]) % (10 ** 9 + 7)
return result
memoization = {}
def repeatedSquaring(a, x):
if x == 0:
# Identity matrix with 1 on the diagonal and 0 elsewhere. X * Identity = X for all X.
return [[1 if x == y else 0 for y in range(26)] for x in range(26)]
if x == 1:
return a
square = a
multiplications = 1
while 2 * multiplications <= x:
double = 2 * multiplications
if double in memoization:
square = memoization[double]
else:
square = matrixMultiply(square, square)
memoization[double] = square
multiplications = double
remainder = repeatedSquaring(a, x - multiplications)
product = matrixMultiply(square, remainder)
memoization[x] = product
return product
transformMatrix = [[0 for _ in range(26)] for _ in range(26)]
for ind in range(26):
for shift in range(nums[ind]):
transformMatrix[ind][(ind + shift + 1) % 26] += 1
result = [0] * 26
for letter in s:
result[ord(letter) - ord('a')] += 1
result = [result]
transforms = repeatedSquaring(transformMatrix, t)
result = matrixMultiply(result, transforms)
count = 0
for i in range(len(result)):
for j in range(len(result[i])):
count = (count + result[i][j]) % (10 ** 9 + 7)
return count