DP solution

class Solution:
# time O(MN) space O(MN), can be optimized to min(M,N)
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[0 for _ in range(n)] for _ in range(m)]
        for i in range(m):
            for j in range(n):
                if i == 0 or j == 0:
                    dp[i][j] = 1
                else:
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
        return dp[-1][-1]

Math solution

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        N = m + n - 2 # total steps
        k = m -1  # steps need to go down 
        res = 1 
        # combination(N,k) = n! / (k!(N-k)!)
        # C = ((N-k+1)(N-k+2)(N-k+3)*...*N)/ k!
        for i in range(1,k+1):
            res = res * (N - k + i)/ i 
        return int(res)