2

Ques: Given a string as input, move all the alphabets in uppercase to the end of the string. Example:

move("Hello World")="ello orldHW"

Problem is: my code doesn't stop at ello orldHW but it continues to

ello orldHW // Expected output   
ello orldWH // What I am actually getting

Code:

public class MoveUppercaseChars {   
    static String testcase1 = "Hello World";

    public static void main(String args[]){
       MoveUppercaseChars testInstance = new MoveUppercaseChars();
       String result = testInstance.move(testcase1);
       System.out.println("Result : "+result);
    }

    public String move(String str){
       int len = str.length();
       char ch;
       for(int i=0; i<len; i++) {
          ch = str.charAt(i);
          if(((int)ch >= 65) && ((int)ch <= 90)) {
             str = str.substring(0, i) + str.substring(i+1, len) + str.charAt(i);
          }         
       }
       return str;
   }    
}
anubhava
  • 761,203
  • 64
  • 569
  • 643
abhishek14d
  • 89
  • 2
  • 12
  • I don't understand the difference between your expected output and actual output. Do you want to say that it is printing it twice? – Rohit Jain Aug 22 '13 at 17:15
  • 1
    The WH is reveresed, and it does appear to be printing out twice. – Andrew_CS Aug 22 '13 at 17:18
  • 1
    Forget this statement System.out.println(str); and take "My name is James Bond" as input. Expected Output: y name is ames ondMJB Actual Output: y name is ames ondJMB I hope you understand my ques now. – abhishek14d Aug 22 '13 at 17:22

8 Answers8

4

Store the Lower Case characters and Upper Case separately, then, return the concatenation of both:

public class MoveUppercaseChars {   

static String testcase1 = "Hello World";

public static void main(String args[]){
    MoveUppercaseChars testInstance = new MoveUppercaseChars();
    String result = testInstance.move(testcase1);
    System.out.println("Result : "+result);
}

public String move(String str){
    int len = str.length();
    String low = "";
    String cap = "";
    char ch;
    for(int i=0; i<len; i++)
    {
        ch = str.charAt(i);
        if(((int)ch >= 65) && ((int)ch <= 90))
        {
            cap  += ch;
        }   
        else {
            low += ch;
        }
    }
    return low + cap;
}   
}
abhishek14d
  • 89
  • 2
  • 12
Math
  • 3,334
  • 4
  • 36
  • 51
  • 1
    For input "I live in New Delhi" output should be "live in ew elhiIND" but its giving "live in ew elhiNID". I hope now you understand what's the issue is. – abhishek14d Aug 22 '13 at 17:21
  • @abhishek14d I haven't figured out at first. I corrected my code. Try it. – Math Aug 22 '13 at 17:31
  • Hey! I naswered that first and with string buffer which is more efficient! so unfair... – Daren Aug 22 '13 at 18:43
  • 1
    @Daren Sorry, but I didn't look at yours to copy, I was developing my own code on eclipse. And they're not really the same, mine is cleaner for beginners, despite yours might be more intelligent or even more efficient, not sure. – Math Aug 22 '13 at 18:53
  • @Math probably the compiler migth change that to string buffer anyways, but if it doesn't, string in Java are immutable, so you are creating a lot of strings each time you add each char. Seems wastefull, and more work for the garbage collector. We had the same idea. You can give me a +1 though. :) – Daren Aug 22 '13 at 20:50
  • @Math thank you very much. Check this quick question to know a bit more about why to use StringBuffer: http://stackoverflow.com/q/65668/1761749 SO rocks... :) – Daren Aug 23 '13 at 09:38
4

I'd use an auxilary string buffer to store the Uppercase in the correct order: and even the lower cases too, so you create less String instances.

public String move(String str){
    char ch;
    int len = str.length();
    // we initialize the buffers with the size so they won't have to be increased
    StringBuffer sbUpperCase = new StringBuffer(len+1);
    StringBuffer sbLowerCase = new StringBuffer(len+1);

    for(int i=0; i<len; i++)
    {
        ch = str.charAt(i);

        //if it is an upperCase letter (but only of the normal ones
        if(Character.isUpperCase(ch))
        {
            sbUpperCase.append(ch);
        }   else {
            sbLowerCase.append(ch);
        }        
    }
    return sbLowerCase.append(sbUpperCase).toString();
} 

Edited with an the Eclipse IDE for better formatting and to use Character.isUpperCase(ch) to check if uppercase. On why it is useful to use StringBuffer instead of the + operator between Strings, check this question: Why to use StringBuffer in Java instead of the string concatenation operator

Community
  • 1
  • 1
Daren
  • 3,337
  • 4
  • 21
  • 35
3

Most simple & smallest code solution:

public String move(String str) {
    return s.replaceAll("[A-Z]+", "") + s.replaceAll("[^A-Z]+", "");
}

Non-Regex based solution:

Using StringBuilder this algorithm can be made very simple:

public String move(String str){
    StringBuilder sb = new StringBuilder(str);
    int d=0;
    for(int i=0; i<str.length(); i++) {
        int ch = str.charAt(i);
        if(ch >= 65 && ch <= 90) {
            sb.deleteCharAt(i-d++).append((char)ch);
        }           
    }
    return sb.toString();
}  

This will be much more efficient also as compared to manipulating immutable String objects multiple times.

anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 1
    Similar to the original code, this will skip the 2nd of any pair of uppercase characters. – James Montagne Aug 22 '13 at 17:41
  • Unless I'm looking at that wrong somehow, I don't think that works. You're checking `str.charAt(i)` but then deleting `i-d`. Perhapse `int ch = str.charAt(i-d)`? – James Montagne Aug 22 '13 at 17:50
  • @JamesMontagne: `d` (deleted counter) starts with 0 and with every deletion it gets incremented to offset deleted character from buffer. See this **working demo: http://ideone.com/phnKA2** – anubhava Aug 22 '13 at 17:52
  • 2
    My mistake, you're looking at the characters in `str` so the manipulation of `sb` doesn't affect the index there. I take that back. +1 – James Montagne Aug 22 '13 at 17:56
  • @abhishek14d I just thought of another very small solution: `s.replaceAll("[A-Z]+", "") + s.replaceAll("[^A-Z]+", "");` – anubhava Aug 22 '13 at 18:04
  • @anubhava I appreciate your answer but I am restricted to not using the API. – abhishek14d Aug 22 '13 at 18:29
  • @abhishek14d: Which API you're not allowed to use, you were already using String class and my first solution is only using just String class? Do you mean you are restricted to use regex? In any case I've provided non regex solution as well. – anubhava Aug 22 '13 at 18:43
2

Change your loop to start at the end of the string, and then work backwards. Also, use an array of char[], it will be faster than building a new String in each iteration of the loop. Something like:

EDIT: This is possibly the most efficient way of doing it:

    char[] input = str.toCharArray();
    int c = input.length; // cursor to start at
    int ip = input.length - 1; // insertion point of next UP character.
    while (c > 0) {
        c--;
        if (Character.isUpperCase(input[c])) {
            char tmp = input[c];
            System.arraycopy(input, c + 1, input, c, ip - c);
            input[ip] = tmp;
            ip--;
        }
    }
    return new String(input);

EDIT: The following loop is not the most efficient...... so moving this code example down.

boolean gotUC=true; // do we have upper-case letters, initialize to true
char[] input = str.toCharArray();
int len = input.length;
while (len > 1 && gotUC) {
    len--;
    int c = len;
    while (c > 0 && !Character.isUpperCase(input[c])) {
       c--;
    }
    if (c >= 0) {
        // move the UC Char to before previous UCase letters....
        char tmp = input[c];
        System.arraycopy(input, c + 1, input, c, len - c);
        input[len] = tmp;
    } else {
        gotUC = false;
    }
}
return new String(input);
rolfl
  • 17,539
  • 7
  • 42
  • 76
  • Your comment makes no sense to me.... If you are worried about the Character.isUpperCase(), then just change that to be your test: input[c] >= 'A' && input[c] <= 'Z' – rolfl Aug 22 '13 at 18:14
1

The problem is that the word H is getting processed twice and during 2nd processing its getting pushed to the end

You may want to keep track of total UPPER CASE words processed, so that they don't get processed again

Also, you can use Character.isUpperCase(ch) to check if a character is upper case

public class Test {

    public static void main(String[] args){
        System.out.println(move("Hello World"));
    }

    public static int getUpperCaseLetters(String str) {
        int r = 0;
        for(char c : str.toCharArray()) {
            if(Character.isUpperCase(c)) {
                r++;
            }
        }
        return r;
    }

    public static String move(String str){
        int len = str.length();
        char ch;
        int totalUppercase = getUpperCaseLetters(str);
        int processed = 0;
        for(int i=0; i<len && processed < totalUppercase; i++)
        {
            ch = str.charAt(i);
            if(Character.isUpperCase(ch))
            {
                str = str.substring(0, i) + str.substring(i+1, len) + str.charAt(i);                
                System.out.println(str);
                processed++;
            }           
        }
        return str;
    }   
}
sanbhat
  • 17,522
  • 6
  • 48
  • 64
  • This will skip over the 2nd uppercase letter for any consecutive uppercase letters (This is also true of the original code). http://ideone.com/0VBntr – James Montagne Aug 22 '13 at 17:35
1
public String move(String str) {
    int todo = str.length();
    int i = 0;
    while (i < todo)
    {
        char c = str.charAt(i);
        if (c >= 65 && c <= 90) {
            str = str.substring(0, i) + str.substring(i + 1, str.length())
                    + str.charAt(i);
            --todo;
            --i;
        }
        ++i;
    }
    return str;
}

This works without an additional String var. Basic idea: If you put an upper case char to the end of the string, you know that you don't need to go to the end of the string. Therefore the limit is initially str.length() which later decrements.

Also if you find a match you have to check that exact position again (therefore --i). Try "HEllo WOrld" with your code or other code snippets.

Marc
  • 6,051
  • 5
  • 26
  • 56
0

I would loop over the input string twice, first copying out the lower-case letters, then copying out the upper-case letters.

public static String move(String str) {
    char[] input = str.toCharArray();
    char[] result = new char[input.length];
    int index = 0;

    for (char current : input)
        if (!Character.isUpperCase(current))
            result[index++] = current;

    for (char current : input)
        if (Character.isUpperCase(current))
            result[index++] = current;

    return new String(result);
}
Toxaris
  • 7,156
  • 1
  • 21
  • 37
0
public class MoveUpperCaseToEnd {

public static void main(String[] args) {
    String string = "SoftwareEngineer";
    String result = moveUpperCase(string);
    System.out.println(result);

}

public static String moveUpperCase(String s) {
    String lowerCase = "";
    String upperCase = "";
    for (int i = 0; i < s.length(); i++) {
        char ch = s.charAt(i);
        if (ch >= 'A' && ch <= 'Z') {
            upperCase += ch;
        } else {
            lowerCase += ch;
        }
    }
    return lowerCase + upperCase;
}

}

Ajay
  • 917
  • 3
  • 12
  • 26