[LintCode] Find the Missing Number II
Giving a string with number from 1 to n in random order, but miss 1
number.Find that number.
You can assume n <= 30
ExampleGiven n = 20
, str = 19201234567891011121314151618
return 17
Solution 1. DFS
Algorithm.
1. set a 1D boolean array: exist[0........n] to track a number from 1 to n is found or not.
2. starting from index i, get 2 digits d1 and d2 and do the following in order.
a. d1 == 0: return false for invalid split;
b. d1 != 0 && exist[d1] == false: get a valid unchecked 1 digit number, set exist[d1] to true and keep searching from index i + 1;
if the searching from i + 1 returns true, then the current search from i also returns true;
if it returns false, backtrack exist[d1] to false and proceed to case c.
c. d <= n && exist[d] == false: get a valid unchecked 2 digits numbers, set exist[d] to true and keep searching from index i + 2;
if the searching from i + 2 returns true, then the current search from i also returns true;
if it returns false, backtrack exist[d] to false and the current search from i returns false as we‘ve already exhausted all possible
splits without finding a valid split.
3. After the search from index 0 finishes, the boolean array exist is correctly updated. Scan from index 1 to n and find the index k where exist[k] = false.
Return k as the missing number.
1 public class Solution { 2 /** 3 * @param n an integer 4 * @param str a string with number from 1-n 5 * in random order and miss one number 6 * @return the missing integer 7 */ 8 public int findMissing2(int n, String str) { 9 if(n < 1 || str == null){ 10 return 0; 11 } 12 boolean[] exist = new boolean[n + 1]; 13 for(int i = 0; i <= n; i++){ 14 exist[i] = false; 15 } 16 dfs(str, 0, exist, n); 17 int val = 1; 18 for(; val <= n; val++){ 19 if(!exist[val]){ 20 break; 21 } 22 } 23 return val; 24 } 25 private boolean dfs(String str, int startIdx, boolean[] exist, int maxNum){ 26 if(startIdx >= str.length()){ 27 return true; 28 } 29 if(startIdx == str.length() - 1){ 30 int d = str.charAt(startIdx) - ‘0‘; 31 if(d == 0 || exist[d]){ 32 return false; 33 } 34 else{ 35 exist[d] = true; 36 return true; 37 } 38 } 39 int d1 = str.charAt(startIdx) - ‘0‘; 40 int d2 = str.charAt(startIdx + 1) - ‘0‘; 41 int d = d1 * 10 + d2; 42 //first digit is 0, no valid way to keep searching 43 if(d1 == 0){ 44 return false; 45 } 46 //can get a valid unchecked 1 digit number 47 if(!exist[d1]){ 48 exist[d1] = true; 49 if(dfs(str, startIdx + 1, exist, maxNum)){ 50 return true; 51 } 52 exist[d1] = false; 53 } 54 //can get a valid unchecked 2 digits number 55 if(d <= maxNum && !exist[d]){ 56 exist[d] = true; 57 if(dfs(str, startIdx + 2, exist, maxNum)){ 58 return true; 59 } 60 exist[d] = false; 61 } 62 //can‘t get either a 1 or 2 digits unchecked number 63 return false; 64 } 65 }
To correctly get the missing number, we should check the possiblity of splitting 1 digit before checking splitting 2 digits. Swaping the order of these
two steps yields incorrect answer. The reason for this is discussed in solution 2.
Solution 2. DFS
Solution 1 uses the condition n <= 30, so when deciding whether keep searching, it only checks the next 2 digits. The disadvantage of this solution
is that it does not extend well if n is changed to more than 2 digits.
Solution 2 does not use the condition that n can have at most 2 digits, so it is a better in this matter.
The algorithm of this solution is as follows.
1. starting from the current digit, check if it is 0; if it is 0, return as we hit an invalid splitting.
2. if it is not 0 and <= n, set its checked flag to true and keep searching starting from the next digit.
3. after done searching, backtrack the current number‘s checked flag to false.
4. include the next digit to the current number; repeat steps 2 and 3 until the current number is > n.
Important note:
To get the correct result, a found boolean flag must be used to make sure a correct answer does not
get overwritten by an incorrect answer.
For example, str = "1234567891011" and n = 12.
The only right split is {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} and the missing integer is 12;
Another split of {12, 3, 4, 5, 6, 7, 8, 9, 10, 11} also reaches the end of str since each splitted number is
in [1, 12]. However, this is not a right split as there are two missing numbers 1 and 2, which contradicts
the problem assumption. Any wrong splits that can reach the end of str always has at least two missing
numbers.
At some point, if splitting one digit and two digits can both reach the end of str, the one digit split is
always the right split and it always set the final answer first. As a result, this solution uses a boolean flag
to ensure only the first reach of str‘s end updates the final answer.
1 public class Solution { 2 private boolean found = false; 3 private int ans = 0; 4 public int findMissing2(int n, String str) { 5 boolean[] exist = new boolean[n + 1]; 6 dfs(0, n, str, exist); 7 return ans; 8 } 9 10 private void dfs(int startIdx, int maxNum, String s, boolean[] exist) { 11 if (startIdx >= s.length()) { 12 if(!found){ 13 for (int k = 1; k <= maxNum; k++) { 14 if (!exist[k]) { 15 ans = k; 16 break; 17 } 18 } 19 found = true; 20 } 21 return; 22 } 23 int sum = s.charAt(startIdx) - ‘0‘; 24 if (sum == 0) { 25 return; 26 } 27 while (sum <= maxNum) { 28 if (!exist[sum]) { 29 exist[sum] = true; 30 dfs(startIdx + 1, maxNum, s, exist); 31 exist[sum] = false; 32 } 33 startIdx++; 34 if (startIdx >= s.length()) { 35 break; 36 } 37 sum = sum * 10 + (s.charAt(startIdx) - ‘0‘); 38 } 39 } 40 }
Related Problems
Decode Ways
Find the Missing Number
[LintCode] Find the Missing Number II