Topics › Strings › Basic and Easy String Problems
Largest Odd Number in a String
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a string num that represents a non-negative integer written in decimal digits. Among all non-empty substrings of num that represent an odd number, return the one with the largest value, as a string. If no substring is odd, return the empty string "".
Constraints: 1 ≤ |num| ≤ 10^5, num consists only of digits 0-9 and has no leading zeros. The number can have a hundred thousand digits, so it does not fit in any primitive type; the answer must be produced as a string.
Example 1
num = "52"
answer = "5" // the odd substrings are "5" only; "52" and "2" are even
Example 2
num = "4206"
answer = "" // every digit is even, so every substring is even
Example 3
num = "35427"
answer = "35427" // the whole string ends in 7, which is odd, and nothing is larger
Approach 1: Brute force, try every substring
Intuition. An integer is odd exactly when its last digit is odd, so a substring num[i..j] is odd exactly when num[j] is 1, 3, 5, 7 or 9. The direct method enumerates all n(n+1)/2 substrings, keeps the ones that end in an odd digit, and remembers the largest. The one non-obvious part is comparing two candidates: they can have thousands of digits, so parsing them into long is not an option. Two digit strings without leading zeros compare by length first and then lexicographically, so we strip leading zeros (a substring such as "023" can have them even though num itself does not) and apply that rule.
Algorithm.
- For every start
iand every endj ≥ i: - If
num[j]is odd, takecand = num.substring(i, j + 1). - If
bestis empty orcandis numerically greater thanbest, setbest = cand. - Return
best.
static String bruteForce(String num) {
String best = "";
int n = num.length();
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
if ((num.charAt(j) - '0') % 2 == 1) { // num[i..j] ends in an odd digit, so it is odd
String cand = num.substring(i, j + 1);
if (best.isEmpty() || isGreater(cand, best)) best = cand;
}
}
}
return best;
}
static boolean isGreater(String a, String b) {
String x = stripLeadingZeros(a), y = stripLeadingZeros(b);
if (x.length() != y.length()) return x.length() > y.length();
return x.compareTo(y) > 0;
}
static String stripLeadingZeros(String s) {
int k = 0;
while (k < s.length() - 1 && s.charAt(k) == '0') k++;
return s.substring(k);
}
Complexity. Time O(n^3): O(n^2) substrings, and each comparison (with its substring copy) costs O(n). Space O(n) for the current candidate and the best so far.
For n = 10^5 this is hopeless, but it is a useful baseline: it is obviously correct, and the tests in the Java file run both methods against the same expected answers.
Approach 2: Optimal, the prefix ending at the rightmost odd digit
Intuition. Two observations shrink the search space to a single candidate.
First, fix the position j of the last digit. Among all substrings that end at j, the longest one, num[0..j], is the largest: it has the most digits, and because num has no leading zeros its first digit is non-zero, so more digits really means a bigger number. So only prefixes matter.
Second, among the prefixes that end in an odd digit, the longest one is the largest, for the same reason. The longest such prefix ends at the rightmost odd digit. So the answer is num.substring(0, i + 1) where i is the last index holding an odd digit, or "" if there is none.
Scanning from the right also has a nice practical property: most random digit strings have an odd digit within the last couple of positions, so the loop usually stops almost immediately.
Algorithm.
- For
ifromn - 1down to0: - If
num[i]is odd, returnnum.substring(0, i + 1). - If the loop finishes, return
"".
static String optimal(String num) {
for (int i = num.length() - 1; i >= 0; i--) {
if ((num.charAt(i) - '0') % 2 == 1) return num.substring(0, i + 1);
}
return "";
}
Dry run on num = "123456":
| i | num[i] | odd? | action |
|---|---|---|---|
| 5 | 6 | no | continue |
| 4 | 5 | yes | return num.substring(0, 5) = "12345" |
And on num = "4206": 6, 0, 2, 4 are all even, the loop runs out, and the answer is "".
Complexity. Time O(n) in the worst case (all digits even, or the only odd digit is the first one). Space O(1) extra; the returned substring is the answer itself and is required by the problem.
Why the brute force and the optimal answer agree
It is worth seeing why the two methods always produce the same string, not just the same numeric value. The optimal prefix P = num[0..i] is the unique longest odd substring: any odd substring ends at some odd index j ≤ i, so its length is at most i + 1, with equality only for P itself. A substring with fewer digits after stripping zeros is numerically smaller than P (which has no zeros to strip), and the brute force only replaces best when a candidate is strictly greater. Hence the brute force settles on P as well, never on an equal-valued variant such as "023" versus "23".
Edge cases and pitfalls
- No odd digit at all (
"4206","2468"). Return"", not"0"and notnull. - Single digit.
"7"returns"7";"8"returns"". - The whole string is odd (
"35427"). The loop stops ati = n - 1and returnsnumunchanged. - Overflow. Never parse the substrings.
Long.parseLongfails beyond 19 digits, and the test with the 22-digit string"1000000000000000000001"exists precisely to catch that mistake. Compare digit strings by length and then lexicographically instead. - Testing oddness.
(num.charAt(i) - '0') % 2 == 1converts the character to its digit first. Because the ASCII codes of'0'to'9'are48to57,(num.charAt(i) & 1) == 1also works, but the explicit conversion is clearer. Do not writenum.charAt(i) % 2 == 1and expect it to read as a digit test: it happens to work only because'0'is even. - Leading zeros inside substrings. The input has none, but substrings can (for example
"05"inside"105"). The brute force strips them before comparing; the optimal method never produces one because it always starts at index 0. - Returning a substring, not an index. The question asks for the number as a string.
substring(0, i + 1)includes positioni.
Related problems
- Longest Common Prefix is the next problem on the sheet and is also about prefixes of strings.
- Reverse words in a given string / Palindrome Check uses the same “scan from the right” idea for a different purpose.
- Rotate String is another problem whose brute force enumerates substrings and whose optimal solution needs a single observation.