CodingBat: Java. String-3, Part I

When I began working on the String-3 section of CodingBat, I felt mislead by the description, which said “Harder String problems — 2 loops.” However, most problems can be solved by only using one loop. The difficulty of the exercises is quite uneven, so don’t get frustrated if some take considerably more time than others.

All solutions were successfully tested on 23 February 2013.

countYZ:

public int countYZ(String str) {
	int count = 0;
	str = str.toLowerCase() + " ";
	for (int i = 0; i < str.length() - 1; i++)
		if ((str.charAt(i) == 'y' || str.charAt(i) == 'z')
				&& !Character.isLetter(str.charAt(i + 1)))
			count++;
	return count;
}

The string manipulation in line 3 takes care of the edge case where the last letter in the string is checked. In my opinion, this is cleaner than adding an extra condition in the for loop.

withoutString:

public String withoutString(String base, String remove) {
	String result = "";
	int index = base.toLowerCase().indexOf(remove.toLowerCase());
	while (index != -1) {
		result += base.substring(0, index);
		base = base.substring(index + remove.length());
		index = base.toLowerCase().indexOf(remove.toLowerCase());
	}
	result += base;
	return result;
}

equalIsNot:

public boolean equalIsNot(String str) {
	int countIs = 0;
	int countNot = 0;
	str = str + "X";	
	for (int i = 0; i < str.length() - 2; i++) {
		if (str.substring(i, i + 2).equals("is")) countIs++;
		if (str.substring(i, i + 3).equals("not")) countNot++;
	}
	return (countIs == countNot);
}

The string manipulation in line 4 avoids a second for loop. By lengthening the string by a character that is not in “is” or “not”, it’s possible to do all necessary checks in just one pass.

gHappy:

public boolean gHappy(String str) {
	str = "X" + str + "X"; // for corner cases
	for (int i = 1; i < str.length() - 1; i++)
		if (str.charAt(i) == 'g' && str.charAt(i - 1) != 'g'
				&& str.charAt(i + 1) != 'g')
			return false;
	return true;
}

countTriple:

public int countTriple(String str) {
	int count = 0;
	for (int i = 0; i < str.length() - 2; i++)
		if (str.charAt(i) == str.charAt(i + 1)
				&& str.charAt(i + 1) == str.charAt(i + 2))
			count++;
	return count;
}

sumDigits:

public int sumDigits(String str) {
	int sum = 0;
	for (int i = 0; i < str.length(); i++)
		if (Character.isDigit(str.charAt(i)))
			sum += Integer.parseInt(str.substring(i, i + 1));
	return sum;
}

15 thoughts on “CodingBat: Java. String-3, Part I

  1. Mike

    For the withoutString problem, I think that using the index method is against the spirit of exercise. My solution is a lot longer, but it forced to think more deeply about the problem.

    public String withoutString(String base, String remove) {
    int len=remove.length();
    String result=””;
    for (int i=0;i<len;i++){
    base+=" ";
    }
    String remove1=remove.toLowerCase();
    String remove2=remove.toUpperCase();
    for (int i=0;i<base.length()-len;i++){
    if (base.substring(i,i+len).equals(remove) ||
    base.substring(i,i+len).equals(remove1) ||
    base.substring(i,i+len).equals(remove2)){
    i+=len-1;
    }
    else{
    result+=base.charAt(i);
    }
    }
    return result;
    }

    Reply
  2. Lau Hestbek

    A much shorter solution to withoutString-method would be to use the replaceAll-method that takes a regular expresion as argument, and then use (?i) to make it case-insensitive matching. :

    public String withoutString(String base, String remove) {
    return base.replaceAll(“(?i)”+remove,””);
    }

    Reply
        1. Gregor Ulm Post author

          I use a rather narrow subset of inbuilt methods, namely those that are mentioned by Nick Parlante in either the supplementary articles on his site, or in the commentary of the exercises. Indeed, there is a mountain of difference between using, for instance, charAt() and replaceAll(). Maybe spend a moment or two reflecting on how those methods may be implemented. This might lead to an important realization.

          Reply
  3. Salman Kazmi

    Dear Sir,

    The solution given for sumDigits caters to the sum of all digits in the String. The question however expects to return the sum of numbers separated by digits other than numbers.
    Eg. aa11b33 in your solution returns 8 but it should return 44.

    Pls advice

    Reply
  4. Juha

    WIth a little help from your example, I did equalsIsNot in a bit more future-proof way:


    public boolean equalIsNot(String str) {
    str = str + "x"; // Easier to check until last character
    return appearances("is", str) == appearances("not", str);
    }

    public int appearances(String needle, String hay) {
    int count = 0;
    for(int i=0; i<hay.length() - needle.length(); i++) {
    if(hay.substring(i, i+needle.length()).equals(needle)) {
    count++;
    }
    }
    return count;
    }

    Reply
  5. Jay

    for countYZ, why is there a ” “/(a space) after str.toLowerCase()? I got everything else and turns out if I don’t have the space it’s off by one in a lot of situations.

    Reply
    1. Jay

      nevermind, I got it. You need the space so you can check the last character. Originally I had this:

      str= str.toLowerCase();
      int counter=0;

      for(int i=0; i<str.length()-1;i++)
      {

      if(str.charAt(i)=='y'&& Character.isLetter(str.charAt(i+1))==false ||str.charAt(i)=='z' && Character.isLetter(str.charAt(i+1))==false )
      counter++;

      }

      if(str.charAt(str.length()-1)== 'y' || str.charAt(str.length()-1)== 'z' )
      counter++;

      return counter;

      yours is a much more efficient version. BTW, thanks for all your work!

      Reply
  6. nick woodward

    really like the equalsIsNot solution!

    wouldn’t have thought to use the original string length minus the shorter value to limit the loop, but then search a longer temporary string.

    thanks!

    Reply
  7. Tri

    This is a shorter solution to withoutString, and it doesn’t require storage of a new variable ‘res’.
    public String withoutString(String base, String remove) {
    int index = 0;
    while(index!=-1){
    index = base.toLowerCase().indexOf(remove.toLowerCase());
    if (index!=-1){
    base = base.substring(0,index)+base.substring(index+remove.length());
    }
    }
    return base;
    }

    Reply
    1. Gregor Ulm Post author

      Someone unfamiliar with what this function is supposed to achieve will likely immediately question why you check for the same condition in two places. Indeed, you can solve withoutString() without a nested if-statement. In my solution, I can of course trivially merge lines 9 and 10, which makes my solution as short as yours, and with a simpler execution path. So, much ado about nothing.

      Reply
  8. WOJTEK

    My solution

    public int countYZ(String str) {
    int count=0;
    str=str.toLowerCase();
    str=str.replaceAll(“[0-9]”, “!”);
    String[] arr = str.split(“[^\\w]”);
    for (int i = 0; i < arr.length; i++)
    if(arr[i].endsWith("y") || arr[i].endsWith("z")) count++;
    return count;
    }

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Spammer prevention; the answer is an integer: * Time limit is exhausted. Please reload CAPTCHA.