Related
I am using StringBuilder to change a String input and shift it depending on input. This is for the META coding practice website and I am running into an issue with their two Test cases. One is passing and the other is not.
The expected output is stuvRPQrpq-999.#and the input is abcdZXYzxy-999.# with a shift of 200.
Here is my code
String rotationalCipher(String input, int rotationFactor) {
// Write your code here
int shift = rotationFactor % 26;
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
if (character >= 'a' && character <= 'z') {
character = (char) (character + shift);
if (character > 'z') {
character = (char) (character + 'a' - 'z' - 1);
}
output.append(character);
} else if (character >= 'A' && character <= 'Z') {
character = (char) (character + shift);
if (character > 'Z') {
character = (char) (character + 'A' - 'Z' - 1);
}
output.append(character);
} else if (character >= '0' && character <= '9') {
character = (char) (character + shift);
if (character > '9') {
character = (char) (character + '0' - '9' - 1);
}
output.append(character);
} else {
output.append(character);
}
}
return output.toString();
}
My issue is that I am somehow outputting AAA instead of 999 as far as I can tell from tracing my algo seems solid. I looked through JAVA docs StringBuilder page to see if there was any issue with how I was using it. As far as I can tell it should be good to go.
Could anyone lend me an idea of why my output is the way it is?
Here is the test cases code:
String input_1 = "All-convoYs-9-be:Alert1.";
int rotationFactor_1 = 4;
String expected_1 = "Epp-gsrzsCw-3-fi:Epivx5.";
String output_1 = rotationalCipher(input_1, rotationFactor_1);
check(expected_1, output_1);
String input_2 = "abcdZXYzxy-999.#";
int rotationFactor_2 = 200;
String expected_2 = "stuvRPQrpq-999.#";
String output_2 = rotationalCipher(input_2, rotationFactor_2);
check(expected_2, output_2);
Check your maths
200 % 26 = 18 (shift)
'9' + 18 = 57 + 18 = 75 ('K')
75 + '0' = 75 + 48 = 123 ('{')
123 - '9' = 123 - 57 = 66 ('B')
66 - 1 = 65 ('A')
Now, the problem is, between '9' and 'A' there are 7 other characters, so character = (char) (character + ('0' - '9') - 1); would have to become character = (char) (character + ('0' - '9') - 9); to shift 9 back to 9, but that would screw up you other test case
I don't think ASCII manipulation is the way to go here, as there are characters in-between the digits and the upper and lower cased characters which are going to mess things up as the rotation increases.
In fact, for the digits, you really want to rotate using a factor % 10 instead.
A different approach would be to generate a list of characters and apply a shift to those instead. Now if I was doing this, I'd use List and Collections, but lets assume you can't do that for second, instead, we're going to need to apply a shift to an array, for example...
public String[] rotate(String[] original, int offset) {
if (offset >= 0) {
return positiveRotate(original, offset);
}
return negativeRotate(original, Math.abs(offset));
}
public String[] positiveRotate(String[] original, int offset) {
String[] results = new String[original.length];
int count = original.length - offset;
System.arraycopy(original, count, results, 0, offset);
System.arraycopy(original, 0, results, offset, count);
return results;
}
public String[] negativeRotate(String[] original, int offset) {
String[] results = new String[original.length];
System.arraycopy(original, offset, results, 0, original.length - offset);
System.arraycopy(original, 0, results, original.length - offset, offset);
return results;
}
Now, this has two different methods, one of a "positive" (or "right" shift) and one for a "negative" (or "left" shift). During testing, I found that the you want to "left shift" the array.
Next, we need what we want to shift over...
private String[] digits = "0123456789".split("");
private String[] characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
I've cheated here, you may need to create the array by long hand, but I can't be bothered typing it out.
Please note
I'm using a String array instead of a char array, not hard to change, but I'm been lazy
You could actually do this on the String directly, using things like contains and split to perform the shifting
And then the rotational cipher might look something like...
public String rotationalCipher(String input, int rotationFactor) {
int shift = rotationFactor % 26;
String[] shiftedDigits = rotate(digits, -(rotationFactor % 10));
String[] shiftCharacters = rotate(characters, -(rotationFactor % 26));
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
String value = Character.toString(character);
int index = 0;
if ((index = indexOf(value, digits)) > -1) {
output.append(shiftedDigits[index]);
} else if ((index = indexOf(value, characters)) > -1) {
output.append(shiftCharacters[index]);
} else if ((index = indexOf(value.toUpperCase(), characters)) > -1) {
output.append(shiftCharacters[index].toLowerCase());
} else {
output.append(value);
}
}
return output.toString();
}
protected int indexOf(String value, String[] array) {
for (int index = 0; index < array.length; index++) {
if (array[index].equals(value)) {
return index;
}
}
return -1;
}
Then you could just execute it something like...
System.out.println(" --> All-convoYs-9-be:Alert1.");
System.out.println(" Got " + rotationalCipher("All-convoYs-9-be:Alert1.", 4));
System.out.println("Want Epp-gsrzsCw-3-fi:Epivx5.");
System.out.println("");
System.out.println(" --> abcdZXYzxy-999.#");
System.out.println(" Got " + rotationalCipher("abcdZXYzxy-999.#", 200));
System.out.println("Want stuvRPQrpq-999.#");
Which outputs
--> All-convoYs-9-be:Alert1.
Got Epp-gsrzsCw-3-fi:Epivx5.
Want Epp-gsrzsCw-3-fi:Epivx5.
--> abcdZXYzxy-999.#
Got stuvRPQrpq-999.#
Want stuvRPQrpq-999.#
As I said above, you could just use a String instead of an array, in which case it might look something more like...
private String digits = "0123456789";
private String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public String rotationalCipher(String input, int rotationFactor) {
int shift = rotationFactor % 26;
String shiftedDigits = rotate(digits, -(rotationFactor % 10));
String shiftCharacters = rotate(characters, -(rotationFactor % 26));
StringBuilder output = new StringBuilder();
for (char character : input.toCharArray()) {
String value = Character.toString(character);
int index = 0;
if ((index = digits.indexOf(value)) > -1) {
output.append(shiftedDigits.charAt(index));
} else if ((index = characters.indexOf(value)) > -1) {
output.append(shiftCharacters.charAt(index));
} else if ((index = characters.indexOf(value.toUpperCase())) > -1) {
output.append(Character.toLowerCase(shiftCharacters.charAt(index)));
} else {
output.append(value);
}
}
return output.toString();
}
public String rotate(String original, int offset) {
if (offset >= 0) {
return positiveRotate(original, offset);
}
return negativeRotate(original, Math.abs(offset));
}
public String positiveRotate(String original, int offset) {
String prefix = original.substring(original.length() - offset);
String sufix = original.substring(0, original.length() - offset);
return prefix + sufix;
}
public String negativeRotate(String original, int offset) {
String prefix = original.substring(offset);
String sufix = original.substring(0, offset);
return prefix + sufix;
}
So here is the thing.
I have to write code to show a binary number X's next smallest "code-X number" which is bigger than binary number X.
code-X number is a binary number which have no continuously 1. For example: 1100 is not a code X number because it has 11, and 1001001001 is a code-X number
Here is my code
String a = "11001110101010";
String b = "";
int d = 0;
for(int i = a.length()-1; i>0;i--){
if(a.charAt(i) == '1' && a.charAt(i-1)=='1'){
while(a.charAt(i)=='1'){
b = b + '0';
if(i!=0){i--;}
d++;
}
}
b = b + a.charAt(i);
}
StringBuffer c = new StringBuffer(b);
System.out.println(c.reverse());
I plan on copy the binary string to string b, replace every '1' which next i is '1' into '0' and insert an '1'
like:
1100 ---> 10000
but i have no idea how to do it :)
May you help me some how? Thanks
Try this. This handles arbitrary length bit strings. The algorithm is as follows.
Needed to conditionally modify last two bits to force a change if the number is not a codeEx number. This ensures it will be higher. Thanks to John Mitchell for this observation.
Starting from the left, find the first group of 1's. e.g 0110
If not at the beginning replace it with 100 to get 1000
Otherwise, insert 1 at the beginning.
In all cases, replace everything to the right of the grouping with 0's.
String x = "10000101000000000001000001000000001111000000000000110000000000011011";
System.out.println(x.length());
String result = codeX(x);
System.out.println(x);
System.out.println(result);
public static String codeX(String bitStr) {
StringBuilder sb = new StringBuilder(bitStr);
int i = 0;
int len = sb.length();
// Make adjust to ensure new number is larger than
// original. If the word ends in 00 or 10, then adding one will
// increase the value in all cases. If it ends in 01
// then replacing with 10 will do the same. Once done
// the algorithm takes over to find the next CodeX number.
if (s.equals("01")) {
sb.replace(len - 2, len, "10");
} else {
sb.replace(len- 1, len, "1");
}
while ((i = sb.indexOf("11")) >= 0) {
sb.replace(i, len, "0".repeat(len - i));
if (i != 0) {
sb.replace(i - 1, i + 2, "100");
} else {
sb.insert(i, "1");
}
}
String str = sb.toString();
i = str.indexOf("1");
return i >= 0 ? str.substring(i) : str;
}
Prints
10000101000000000001000001000000001111000000000000110000000000011011
10000101000000000001000001000000010000000000000000000000000000000000
Using raw binary you can use the following.
public static void main(String[] args) {
long l = 0b1000010100000000010000010000000011110000000000110000000000011011L;
System.out.println(
Long.toBinaryString(nextX(l)));
}
public static long nextX(long l) {
long l2 = l >>> 1;
long next = Long.highestOneBit(l & l2);
long cutoff = next << 1;
long mask = ~(cutoff - 1);
return (l & mask) | cutoff;
}
prints
1000010100000000010000010000000010000000000000000000000000000000
EDIT: Based on #WJS correct way to find the smallest value just larger.
This is a slight expansion WJS' 99% correct answer.
There is just one thing missing, the number is not incremented if there are no consecutive 1's in the original X string.
This modification to the main method handles that.
Edit; Added an else {}. Starting from the end of the string, all digits should be inverted until a 0 is found. Then we change it to a 1 and break before passing the resulting string to WJS' codeX function.
(codeX version does not include sb.replace(len-2,len,"11");)
public static void main(String[] args) {
String x = "10100";
StringBuilder sb = new StringBuilder(x);
if (!x.contains("11")) {
for (int i = sb.length()-1; i >= 0; i--) {
if (sb.charAt(i) == '0') {
sb.setCharAt(i, '1');
break;
} else {
sb.setCharAt(i, '0');
}
}
}
String result = codeX(sb.toString());
System.out.println(x);
System.out.println(result);
}
My situation: I have a string (called str). The length of the string must be a multiple of 30 (e.g 30, 60, 90). If the length of the str is not a multiple of 30, add spaces " " to the end of it.
Something like this:
if (str.length() / 30 != ????) {
//add spaces to the end
}
Of course, the code above is not correct. It would be very appreciated if you could help me with this.
Thanks in advance!
You can quite simply do:
if(str.length()%30!=0)
str = String.format("%1$-"+(((str.length()/30)*30)+30)+"s", str);
str.length()%30 gives the remainder on dividing the length by 30. If it is not 0, then you have to add spaces.
String.format("%1$-"+(((str.length()/30)*30)+30)+"s", str) adds the spaces to the right of the String.
Or even simply, you can do:
while(str.length()%30 !=0)
str+= ' ';
this code is not tested but i guess it will work:
int a = str.length()%30;
for(int i=0; i<=a; i++)
str = str + " ";
What would you do to check if a number is a multiple of 30 in simple Maths?
Yes, you would divide and check if the remainder is 0 or not, right?
That's how you'll do it in Java. To get the remainder in Java, Modulus(%) operator is used. So, you can do it like this :
if (str.length() % 30 != 0) {
//add spaces to the end
str += " ";
}
or if you want to add spaces to make the length a multiple of 30, then do this:
int remainder = str.length() % 30;
if (remainder != 0) {
//add spaces to the end
int numSpacesRequired = 30-remainder; //no. of spaces reuired to make the length a multiple of 30
for(int i = 0; i < numSpacesRequired; i++)
str += " ";
}
Read more about the basic operators in Java here.
You can use Modulo to achieve that:
if (str.length() % 30 != 0) {
//add spaces to the end
str += ' ';
}
Simply: (Tested and worked)
public static void main(String[] args) {
String str = "AYOA"; //Length is only 4
//If the remainder of the str's length is not 0 (Not a multiple)
if (str.length() % 30 != 0){
str += ' ';
}
System.out.println("[" + str + "]"); //A space is added at the back
}
If you want to add the space continuously until the length is a multiple of 30:
public static void main(String[] args) {
String str = "AYOA"; //Length is only 4
//If the remainder of the str's length is not 0 (Not a multiple)
//Repeat until is multiple of 30
while(str.length % 30 != 0){
str += ' ';
}
System.out.println("[" + str + "]"); //A space is added at the back
}
use StringBuilder to build whitespaces
String str="multiple of 30";
int spacesNum=str.length()%30; //get the remainder
StringBuilder spaces=new StringBuilder(); //build white spaces
for(int j=0;j<spacesNum;j++){
spaces.append(' ');
}
System.out.println(str+spaces.toString());
I need to add two binary numbers and return the sum. No base conversions are allowed. I know the long method, using arrays. But is there anything shorter ? And by shorter I mean "having smaller code length". Thanks in advance.
In case I was not explicit enough, here is an example:
Input:
1101
11
Output: 10000
The sum of two (binary) integers a and b can be computed as a+b, because all arithmetic is done in binary.
If your input is in human readable strings rather than binary, you can compute their sum in binary using the standard BigInteger class:
import java.math.BigInteger;
String sum(String a, String b) {
return new BigInteger(a, 2).add(new BigInteger(b, 2)).toString(2);
}
Represent the binary numbers as two strings. Reverse the two strings. Then, you can iterate along both strings simultaneously, adding values to three arrays, two which represent the binary digit being added from the strings and the third to represent the carry digit. Create a fourth array representing the answer (you might have to find the limit for how long the answer can possibly be).
fill the answer array by using standard binary adding:
0 + 0 = 0 in the same position,
1 + 0 = 0 + 1 = 1 in the same position,
1 + 1 = 0 in the same position, and carry a 1 to the next position,
1 + 1 + 1 = 1 in the same position, and carry a 1 to the next position.
Reverse the array and you'll have the answer as a binary number.
Here are a couple options, not using any utility methods provided by Java. These don't account for sign (leading +/-) so they only handle whole numbers.
This first method converts the binary strings to integers, adds the integers, then converts the result back to binary. It uses a method-local inner class Convert to avoid duplicating the binaryToInt() code for each of the parameters.
static String binaryAdd1(String binary1, String binary2) {
class Convert {
int binaryToInt(String binary) {
int result = 0;
for (int i = 0; i < binary.length(); i++) {
char c = binary.charAt(i);
result *= 2;
if (c == '1') {
result++;
} else if (c != '0') {
throw new IllegalArgumentException(binary);
}
}
return result;
}
}
final Convert convert = new Convert();
int int1 = convert.binaryToInt(binary1);
int int2 = convert.binaryToInt(binary2);
String result = "";
int temp = int1 + int2;
do {
result = ((temp & 1) == 1 ? '1' : '0') + result;
temp >>= 1;
} while (temp > 0);
return result;
}
This second method uses binary addition logic, as specified by JHaps in his answer, to directly add together the two parameters. No intermediate conversion to integers here.
static String binaryAdd2(String binary1, String binary2) {
final String validDigits = "01";
String binarySum = "";
// pad the binary strings with one more significant digit for carrying
String bin1 = '0' + binary1;
String bin2 = '0' + binary2;
// add them together starting from least significant digit
int index1 = bin1.length() - 1;
int index2 = bin2.length() - 1;
boolean carry = false;
while (index1 >= 0 || index2 >= 0) {
char char1 = bin1.charAt(index1 >= 0 ? index1 : 0);
char char2 = bin2.charAt(index2 >= 0 ? index2 : 0);
if (validDigits.indexOf(char1) < 0)
throw new NumberFormatException(binary1);
if (validDigits.indexOf(char2) < 0)
throw new NumberFormatException(binary2);
if (char1 == char2) {
binarySum = (carry ? '1' : '0') + binarySum;
carry = char1 == '1';
} else {
binarySum = (carry ? '0' : '1') + binarySum;
}
index1--;
index2--;
}
if (binarySum.length() > 1 && binarySum.charAt(0) == '0') {
binarySum = binarySum.substring(1);
}
String result = binarySum.toString();
return result;
}
I am trying to solve a problem . Hoping for some help here.
Objective: Find a number which is immediate next to input and contains only 4 and 7 in it.
Input : 1234
Output: 4444
Input : 4476
output: 4477
Input : 8327
Output : 44444
I am not looking for incrementing number and each time checking string characters for the pattern. That would be too slow for large numbers.
static String toOutput (int a) {
// I am trying here all the possible other ways
}
Check this answer. Hope this helps :)
private static String toOutput(int n) {
String input = String.valueOf(n+1);
// create input character array and output character array of one more in size
char[] inputChars = input.toCharArray();
char[] outputChars = new char[inputChars.length + 1];
boolean extra = false; //carry forward
// traverse input array from last position to first position
for (int i = inputChars.length - 1; i >= 0; i--) {
// for all other positions except last position check whether number is changed
// (i.e. apart from 4 or 7),
// change all higher digits in output array to 4
if ((i + 1) < inputChars.length) {
if (inputChars[i] != '4' && inputChars[i] != '7') {
for (int j = i + 1; j < inputChars.length; j++) {
outputChars[j + 1] = '4';
}
}
}
// if extra is true that means it is carry forward
if (extra == true) {
inputChars[i] = (char) ((int) inputChars[i] + 1);
}
// if input digit is less than equal to 4 output digit is 4 , extra is false
if (inputChars[i] <= '4') {
outputChars[i + 1] = '4';
extra = false;
}
// if input digit is between 4 to 7 output digit is 7 , extra is false
else if (inputChars[i] <= '7') {
outputChars[i + 1] = '7';
extra = false;
}
// if input digit is more than 7 output digit is 4 , extra is true
else {
outputChars[i + 1] = '4';
extra = true;
}
}
// if carry forward is true, make extra digit to 4 otherwise it is not required
if (extra == true) {
outputChars[0] = '4';
} else {
outputChars[0] = ' ';
}
return new String(outputChars).trim();
}
static String toOutput (long a) {
LinkedList<Long> q = new LinkedList<Long>();
q.add(4L);
q.add(7L);
while(!q.isEmpty()){
Long curr = q.pop();
if(curr>a)
return String.valueOf(curr);
q.add(curr*10+4);
q.add(curr*10+7);
}
return "";
}
This will solve the problem in close to O(LogN)
Since this is fundamentally a manipulation of character strings, a plausible solution is to use string functions, particularly regular expressions. Here's a compact solution:
class Incrementer {
Pattern p;
public Incrementer() {
p = Pattern.compile("(?:([47]*)([0-6]))?(.*)");
}
public String next(String s) {
Matcher m = p.matcher(s);
m.lookingAt();
return (m.group(1)==null
? '4'
: m.group(1) + (m.group(2).charAt(0) >= '4' ? '7' : '4'))
+ m.group(3).replaceAll(".", "4");
}
}
See it here.
(I'm not at all a Java programmer. Coding suggestions welcome.)
The regular expression matches the prefix of any sequence of legal digits (4 or 7) followed by an incrementable digit ( < 7). If that prefix is not matchable, the answer must be one digit longer, so it must start with the smallest legal digit (4). If the prefix is matchable, the prefix must be modified by bumping the last digit to the next legal digit. In both cases, all the digits following the (possibly empty) prefix are replaced with the smallest legal digit.
Of course, this could be done without actual regular expressions. The following essentially uses a state machine which implements the regular expression, so it might be faster. (Personally I find the regex version easier to verify, but YMMV):
public static String next(String s)
{
int toinc = -1;
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
if (c < '7') {
toinc = i;
if (c != '4') break;
} else if (c > '7') break;
}
char[] outChars;
// Copy the prefix up to and including the character to be incremented
if (toinc < 0) {
outChars = new char[s.length() + 1];
} else {
outChars = new char[s.length()];
for (int i = 0; i < toinc; ++i)
outChars[i] = s.charAt(i);
// Increment the character to be incremented
outChars[toinc] = s.charAt(toinc) >= '4' ? '7' : '4';
}
// Fill with 4's.
for (int i = toinc + 1; i < outChars.length; ++i)
outChars[i] = '4';
return new String(outChars);
}
See it here.
*
public class PatternTest {
private static final char FOUR = '4';
private static final char SEVEN = '7';
public static void main(String[] args) {
Scanner scanner = new Scanner(new InputStreamReader(System.in));
String value = scanner.next();
char startChar = value.charAt(0);
Result result;
if (startChar == FOUR || startChar == SEVEN) {
result = getStartWith4Or7(value);
} else {
result = getNotStartWith4Or7(value);
}
System.out.println("Final value is : " + result.getValue());
}
private static Result getNotStartWith4Or7(String value) {
Result result = new Result();
char startChar = value.charAt(0);
if (startChar < FOUR) {
result.value = value.replaceAll(".", String.valueOf(FOUR));
} else if (startChar > SEVEN) {
result.value = value.replaceAll(".", String.valueOf(FOUR));
result.flag = FOUR;
} else if (startChar > FOUR && startChar < SEVEN) {
result.value = getSubString(value).replaceAll(".", String.valueOf(FOUR));
result.value = String.valueOf(SEVEN) + result.value;
}
return result;
}
private static Result getStartWith4Or7(String value) {
Result result = new Result();
if (value != null && !value.equalsIgnoreCase("")) {
char startChar = value.charAt(0);
if (startChar == FOUR || startChar == SEVEN) {
value = getSubString(value);
result = getStartWith4Or7(value);
result.value = getStartCharUpdate(startChar, result) + result.value;
} else {
result = getNotStartWith4Or7(value);
}
}
return result;
}
private static String getStartCharUpdate(char startChar, Result result) {
String newValue = String.valueOf(startChar);
if (result.flag == FOUR) {
if (startChar == FOUR) {
newValue = String.valueOf(SEVEN);
result.flag = 0;
} else {
newValue = String.valueOf(FOUR);
}
}
return newValue;
}
private static String getSubString(String value) {
int len = value.length();
String finalValue = "";
if (len > 1) {
finalValue = value.substring(1, len);
}
return finalValue;
}
static class Result {
String value = "";
char flag;
public String getValue() {
if (flag == FOUR) {
value = FOUR + value;
}
return value;
}
}
}
*
You can try something like this.
String num = "4476";
double immediateVal = 0;
int length = num.length();
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < length; i++) {
sb1.append("4");
sb2.append("7");
}
double valNum = Double.parseDouble(num);
double val1 = Double.parseDouble(sb1.toString());
double val2 = Double.parseDouble(sb2.toString());
if (valNum < val1) {
immediateVal = val1;
} else if (val1 <= valNum && valNum < val2) {
if(num.indexOf("4")==0){
int firstIndexOf7=-1;
for(int a=0;a<length;a++){
firstIndexOf7=num.indexOf("7");
if(firstIndexOf7!=-1){
break;
}
}
if(firstIndexOf7!=-1){
StringBuilder sb3=new StringBuilder();
for(int b=0;b<firstIndexOf7;b++){
sb3.append("4");
}
for(int b=firstIndexOf7;b<length;b++){
sb3.append("7");
}
immediateVal=Double.parseDouble(sb3.toString());
}
} else{
immediateVal = val2;
}
}else if(valNum>=val2){
immediateVal=Double.parseDouble(sb1.append("4").toString());
}
System.out.println(immediateVal);
this can help you
static String toOutput(String input){
char[] values = input.toCharArray();
StringBuilder result = new StringBuilder();
int length = values.length;
int currentPosition = 0;
for(char current: values){
Integer currentInt = new Integer(current);
if(currentInt<4){
//fill the string with 4, best result
for(currentPosition;currentPosition<length;currentPosition++){
result.append(4);
}
break;
}
else if(currentInt==4){
result.append(4);
currentPosition++;
continue;
}
else if(currentInt<=7){
result.append(7);
currentPosition++;
continue;
}
else if(currentInt>7){
if(currentPosition=0){
//fill the string with 4 but with length +1, best result
for(currentPosition;currentPosition<=length;currentPosition++){
result.append(4);
}
break;
}
else{
// you need work this case, change last 4 to 7 and fill the rest with 4. enjoy it.
}
}
return result.toString();
}
One approach would be reading the digits right to left and check if that is less than 4 or 7 and add 4 or 7 respectively.
One optimization would be check if first(from left) digit is >7 then its sure that you will have all 4's +1 extra 4`.
You need to take extra care at the left most digit. If the left most digit is greater than 7 you need to add two 4s.
EX: 1234
right to left `4` is `4`
`3` is `4`
`2` is `4`
`1` is `4`
This approach wont work if all the digits in the number are 4 or 7. You need to have one condition and change one or two chars accordingly.
private static String toOutput(int a) {
String s = Integer.toString(a);
StringBuilder sb = new StringBuilder();
if (Integer.valueOf(s.charAt(0)) > 7) {
for(int i=0; i<= s.length(); i++) {
sb.append("4");
}
return sb.toString();
}
for(int i=s.length()-1; i>=0; i--) {
Integer x = Integer.valueOf(i);
if(x <=4) {
sb.append(4);
} else {
sb.append(7);
}
}
return sb.reverse().toString();
}
This is not checking if the number has all 4's or 7's.
I don't think you need to worry about performance for converting a number into a String. You're evaluating a number as if it's a string, so it only makes sense to cast it to string to do the evaluation.
Something like this works and is reasonably fast.
public static int closestNumber(int num) {
for (int i = num + 1; i < Integer.MAX_VALUE; i++) {
if ((i+"").matches("^[47]+$")) {
return i;
}
}
throw new RuntimeException("Your number was too close to Integer's max value!");
}
Ok, I wrote it using mod and no strings:
public static int closestNumber(int num) {
for (int i = num + 1; i < Integer.MAX_VALUE; i++) {
if (containsOnly(i,4,7)) {
return i;
}
}
throw new RuntimeException("Your number was too close to Integer's max value!");
}
public static boolean containsOnly(int evaluate, int numeralA, int numeralB) {
while (evaluate > 0) {
int digit = evaluate % 10;
if (digit != numeralA && digit != numeralB) return false;
evaluate = evaluate / 10;
}
return true;
}
It looks like looking for the first digit that is not 4 or 7 counting from left to right.
Set up a index pointer to record the last index that hold digit "4" and check every digit from left to right.
Initial current pointer index (c) to -1 and set last "4" index (l) to -1
For every digit from left to right
update c (c+=1)
check digit value
digit = 7 -> do nothing
digit = 4 -> l = c
digit < 4 -> this digit change to "4", all remaining digits sets to "4", end check
4 < digit < 7 -> this digit change to "7", all remaining digits sets to "4", end check
digit > 7 -> do necessary change and end check
l = -1 => 444....444 (no. of digit = n+1)
l > -1 => digit at l change to "7", all digits after l change to "4"
Idea
For a n-digit value, if it contains only "4" or "7", you do nothing.
Then, if there is any non "4" or "7", what should it be?
Analyzing the pattern, we need to know the first occurrence of non "4"/"7" digit (from left to right) only and all digits after the digit will change to "4" to minimize the value since 444...444 is the least k-digit value for combination of "4" and "7" for all k.
Consider case Xcccccccc , c is any value
If X in {4,7}, consider case 2.
If X in {1,2,3}, the next value should be 444444444.
If X in {5,6}, the next value should be 744444444.
If X in {8,9}, the next value should be 4444444444
Consider case aaaaXcccc, if a are "4" or "7"
If X in {4,7}, consider case aaaaaXccc.
If X in {0,1,2,3}, the next value should be aaaa44444.
If X in {5,6}, the next value should be aaaa74444.
If X in {8,9}, the next value should be bbbb44444 or bbbbb44444.(b are "4" or "7")
then how to deduce bbbb or bbbbb?
if aaaa does not have any "4", you get bbbbb = 44444 (since aaaa=7777)
if aaaa have "4", you get bbbb ("4" will be replaced by "7", e.g. 474779 => 477444)
Consider case aaaaaaaaX, this should be same as case 2 except no remaining digit need to be handle
Combine case 1-3, when the first occurrence of non "4"/"7" digit is in {8,9}, the difference in change of value depends on whether there is any "4" before the digit.