Find the Largest Palindrome Divisible by K
HardPrompt
You are given two positive integers n and k.
An integer x is called k-palindromic if:
xis a palindrome.xis divisible byk.
Return the largest integer having n digits (as a string) that is k-palindromic.
Note that the integer must not have leading zeros.
Example 1:
Input: n = 3, k = 5
Output: "595"
Explanation:
595 is the largest k-palindromic integer with 3 digits.
Example 2:
Input: n = 1, k = 4
Output: "8"
Explanation:
4 and 8 are the only k-palindromic integers with 1 digit.
Example 3:
Input: n = 5, k = 6
Output: "89898"
Constraints:
1 <= n <= 1051 <= k <= 9
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves generating all possible n-digit palindromes in descending order and checking for divisibility by k. A palindrome is defined by its first half. We can iterate through all possible first halves from largest to smallest, construct the full palindrome, and the first one that is divisible by k will be our answer. This method is straightforward but computationally expensive.
Algorithm
- Calculate the length of the first half of the palindrome,
m = (n + 1) / 2. - Define the search range for the first half: from
10^m - 1down to10^(m-1). - Iterate through each number
iin this range in descending order. - For each
i, convert it to its string representations. - Construct the full palindrome string
p_strby concatenatingswith the appropriate part of its reverse. - Convert
p_strto aBigInteger. - Check if the
BigIntegeris divisible byk. - If it is, return
p_stras it is the largest possible solution.
Walkthrough
The core idea is that an n-digit palindrome is uniquely determined by its first ceil(n/2) digits. Let's call this the "first half". To find the largest palindrome, we should start with the largest possible first half and work our way down.
The algorithm proceeds as follows:
- The length of the first half is
m = (n + 1) / 2. - The largest number with
mdigits is10^m - 1. The smallest is10^(m-1)(to avoid leading zeros in the final palindrome). - We iterate a number
ifrom10^m - 1down to10^(m-1). - For each
i, we construct the full palindrome. Ifias a string iss, the palindrome iss + reverse(s)(with the middle digit ofsnot repeated ifnis odd). - Since
ncan be up to10^5, the resulting palindrome can be very large. We must use aBigIntegerto handle the number and its divisibility check (palindrome.mod(k) == 0). - The first number
ithat generates a palindrome divisible bykgives us the largest such palindrome, so we can return it immediately.
import java.math.BigInteger; class Solution { public String largestKPalindromic(int n, int k) { int halfLen = (n + 1) / 2; // The problem constraints ensure n>=1, so halfLen-1 >= 0 BigInteger start = BigInteger.TEN.pow(halfLen - 1); BigInteger end = BigInteger.TEN.pow(halfLen).subtract(BigInteger.ONE); BigInteger bigK = BigInteger.valueOf(k); for (BigInteger i = end; i.compareTo(start) >= 0; i = i.subtract(BigInteger.ONE)) { String firstHalf = i.toString(); StringBuilder secondHalfBuilder = new StringBuilder(firstHalf).reverse(); String palindromeStr; if (n % 2 == 1) { palindromeStr = firstHalf + secondHalfBuilder.substring(1); } else { palindromeStr = firstHalf + secondHalfBuilder.toString(); } BigInteger palindromeNum = new BigInteger(palindromeStr); if (palindromeNum.mod(bigK).equals(BigInteger.ZERO)) { return palindromeStr; } } // According to problem constraints, a solution always exists. // This part of the code should be unreachable. return ""; }}Complexity
Time
O(10^(n/2) * n). The loop runs approximately `9 * 10^((n/2)-1)` times. Inside the loop, creating and checking a palindrome of length `n` takes `O(n)` time with `BigInteger` operations.
Space
O(n) to store the palindrome string and the `BigInteger` representation.
Trade-offs
Pros
Simple to understand and implement.
Cons
Extremely inefficient due to its exponential time complexity.
Times out for values of
ngreater than approximately 15.
A highly efficient approach is to construct the first half of the palindrome digit by digit, from left to right (most significant to least significant). We can use dynamic programming with memoization to find the largest possible first half that results in a full palindrome divisible by k.
Algorithm
- Precompute the weights
W_ifor each digith_iof the first half. This requires precomputing powers of 10 modulok. - Define a recursive function
solve(idx, rem)that returnstrueif a valid suffix for the first half (fromidxtom-1) can be formed, given the current remainderrem. - The recursion's base case is
idx == m. It returnstrueifrem == 0,falseotherwise. - In
solve(idx, rem), iterate through possible digitsdfrom 9 down to 0. The first digit (idx=0) cannot be 0. - For each
d, calculate thenew_rem = (rem + d * W[idx]) % kand recursively callsolve(idx + 1, new_rem). - If the recursive call is successful, it means
dis the largest possible digit for this position. Storedand returntrue. - Use a 2D array
memo[idx][rem]for memoization to store the results ofsolve. - After the main call
solve(0, 0)completes, reconstruct the first half string by backtracking through the stored choices. - Construct the full palindrome from the first half.
Walkthrough
The palindrome P can be expressed as a weighted sum of the digits h_0, h_1, ..., h_{m-1} of its first half: P = sum(h_i * W_i), where m = (n+1)/2. The weight W_i for digit h_i depends on its position. For a pair of digits (d_{n-1-i}, d_i), where d_{n-1-i} = d_i = h_i, the weight is W_i = 10^{n-1-i} + 10^i. For the middle digit in an odd-length palindrome, the weight is just its positional value 10^{m-1}.
We need P % k == 0, which translates to (sum(h_i * W_i)) % k == 0.
We can define a recursive function solve(idx, rem) that tries to build the suffix of the first half from index idx onwards, given that the prefix has accumulated a remainder rem modulo k. To find the largest palindrome, for each position idx, we try digits d from 9 down to 0. We greedily pick the first (largest) d for which solve(idx + 1, new_rem) returns true. We use a memoization table memo[idx][rem] to store the results of subproblems to avoid recomputation. A separate table path[idx][rem] can store the chosen digit d to reconstruct the solution.
class Solution { private int n; private int k; private int m; private int[] W; private int[][] memo; private int[][] path; public String largestKPalindromic(int n, int k) { this.n = n; this.k = k; this.m = (n + 1) / 2; W = new int[m]; int[] pow10 = new int[n + 1]; pow10[0] = 1 % k; for (int i = 1; i <= n; i++) { pow10[i] = (pow10[i - 1] * 10) % k; } for (int i = 0; i < m; i++) { if (n % 2 == 1 && i == m - 1) { // Middle digit for odd n W[i] = pow10[i]; } else { W[i] = (pow10[i] + pow10[n - 1 - i]) % k; } } memo = new int[m + 1][k]; // 0: uncomputed, 1: true, -1: false path = new int[m][k]; solve(0, 0); StringBuilder firstHalf = new StringBuilder(); int currentRem = 0; for (int i = 0; i < m; i++) { int digit = path[i][currentRem]; firstHalf.append(digit); currentRem = (currentRem + (int)((long)digit * W[i] % k) + k) % k; } String firstHalfStr = firstHalf.toString(); if (firstHalfStr.length() > 0 && firstHalfStr.charAt(0) == '0') { // This case implies no positive solution was found, which shouldn't happen. // A single '0' is a valid 1-digit palindrome divisible by any k. // But problem asks for largest, and positive solutions exist. return "0"; } StringBuilder secondHalf = new StringBuilder(firstHalfStr).reverse(); if (n % 2 == 1) { return firstHalfStr + secondHalf.substring(1); } else { return firstHalfStr + secondHalf.toString(); } } private boolean solve(int idx, int rem) { if (idx == m) { return rem == 0; } if (memo[idx][rem] != 0) { return memo[idx][rem] == 1; } for (int d = 9; d >= 0; d--) { if (idx == 0 && n > 1 && d == 0) continue; // No leading zeros int newRem = (rem + (int)((long)d * W[idx] % k) + k) % k; if (solve(idx + 1, newRem)) { memo[idx][rem] = 1; path[idx][rem] = d; return true; } } memo[idx][rem] = -1; return false; }}Complexity
Time
O(n * k). The number of DP states is `m * k`, which is `O(n*k)`. Each state takes `O(1)` time to compute due to the constant-size loop (10 digits). Precomputation of weights takes `O(n)`.
Space
O(n * k) for the memoization and path tables.
Trade-offs
Pros
Highly efficient with linear time complexity in
n.Guaranteed to find the optimal solution for large constraints.
Cons
More complex to understand and implement compared to brute-force.
Requires more memory to store the DP tables.
Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.