package com.freetymekiyan.algorithms.level.medium; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.util.ArrayDeque; import java.util.Deque; /** * Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is * the smallest possible. * <p> * Note: * The length of num is less than 10002 and will be ≥ k. * The given num does not contain any leading zero. * <p> * Example 1: * <p> * Input: num = "1432219", k = 3 * Output: "1219" * Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest. * <p> * Example 2: * <p> * Input: num = "10200", k = 1 * Output: "200" * Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes. * <p> * Example 3: * <p> * Input: num = "10", k = 2 * Output: "0" * Explanation: Remove all the digits from the number and it is left with nothing which is 0. * <p> * Tags: Stack, Greedy * Similar Problems: (H) Create Maximum Number */ public class RemoveKDigits { private RemoveKDigits r; /** * Stack, Greedy. * Find the last digit of the increasing sequence and the first digit of the decreasing sequence. * Remove it and decrement k by 1. * Why is it greedy? Since operation like this will generate a smallest number for each removal. * Stop till k is zero. * Convert remaining string to a valid number format. */ public String removeKdigits(String num, int k) { // Remove all if (num.length() == k) { return "0"; } Deque<Character> stack = new ArrayDeque<>(); for (int i = 0; i < num.length(); i++) { // Remove digits that are larger than current one in stack while (k > 0 && !stack.isEmpty() && stack.peek() > num.charAt(i)) { stack.pop(); k--; } stack.push(num.charAt(i)); } // If there are too few decreasing sequences while (k > 0) { stack.pop(); k--; } // Convert characters in stack to a string StringBuilder sb = new StringBuilder(); while (!stack.isEmpty()) { sb.append(stack.pop()); } sb.reverse(); // Trim leading zeros while (sb.length() > 1 && sb.charAt(0) == '0') { sb.deleteCharAt(0); } return sb.toString(); } /** * Stack, Greedy. * The only difference is that stack is imitated with an Object(Character) array and a pointer to its top. * Because we know the largest possible size of a stack. */ public String removeKdigitsB(String num, int k) { int len = num.length() - k; char[] stk = new char[num.length()]; int top = 0; for (int i = 0; i < num.length(); i++) { char c = num.charAt(i); while (k > 0 && top > 0 && stk[top - 1] > c) { top--; // Pop k--; } stk[top++] = c; // Push } int offset = 0; while (offset < len && stk[offset] == '0') { offset++; } return offset == len ? "0" // All zeros : new String(stk, offset, len - offset); } @Before public void setUp() { r = new RemoveKDigits(); } @Test public void testExamples() { Assert.assertEquals("1219", r.removeKdigits("1432219", 3)); Assert.assertEquals("200", r.removeKdigits("10200", 1)); Assert.assertEquals("0", r.removeKdigits("10", 2)); Assert.assertEquals("1234", r.removeKdigits("123456", 2)); Assert.assertEquals("5432", r.removeKdigits("765432", 2)); // Test B Assert.assertEquals("1219", r.removeKdigitsB("1432219", 3)); Assert.assertEquals("200", r.removeKdigitsB("10200", 1)); Assert.assertEquals("0", r.removeKdigitsB("10", 2)); Assert.assertEquals("1234", r.removeKdigitsB("123456", 2)); Assert.assertEquals("5432", r.removeKdigitsB("765432", 2)); } @After public void tearDown() { r = null; } }