The problem is that you calculate the size of String
only at the beginning, before the while
. But after the first iteration, the String
will have one character less, and then you will try to catch a substring
until sLength
, which is now larger than the size of String
(after all, you removed a character), and this will cause a StringIndexOutOfBoundsException
. And even if this error did not occur, the program would go into loop infinite, after all sLength
is never updated on while
.
Then one solution would be to upgrade the size of the String
with each iteration. Another detail is that when the size of the String
is par, you are picking up the second character of the middle, so an adjustment has to be made for this case:
static void semLetraMeio(String s) {
System.out.println(s);
int sLength = s.length();
while (sLength > 1) {
int midIndex = sLength / 2;
if (sLength % 2 == 0) { // ajuste para quando o tamanho é par
midIndex--;
}
s = s.substring(0, midIndex).concat(s.substring(midIndex + 1, sLength));
System.out.println(s);
sLength = s.length();
}
}
Another detail is that I changed the return of the method to void
, because it doesn’t seem to make sense for him to return anything (he will return the last substring
found). Well, if you think it makes sense, just put back the return
and the return of the method to String
.
I also put a println
at the beginning to print the String
original. Calling semLetraMeio("rapsodia")
, the exit is:
rapsodia
rapodia
rapdia
radia
raia
ria
ra
a
Another alternative is to use a StringBuilder
, that has the method deleteCharAt
, which serves precisely to erase a character in a given position:
static void semLetraMeio(String s) {
System.out.println(s);
StringBuilder sb = new StringBuilder(s);
while (sb.length() > 0) {
int midIndex = sb.length() / 2;
if (sb.length() % 2 == 0) {
midIndex--;
}
sb.deleteCharAt(midIndex);
System.out.println(sb);
}
}
If you want the method to return one String
, as your original code does, switch to:
static String semLetraMeio(String s) {
System.out.println(s);
StringBuilder sb = new StringBuilder(s);
while (sb.length() > 0) {
int midIndex = sb.length() / 2;
if (sb.length() % 2 == 0) {
midIndex--;
}
sb.deleteCharAt(midIndex);
System.out.println(sb);
}
return sb.toString();
}
In my opinion it would be better to make a recursive call and create fewer variables, like here:
java
static String semLetraMeio(String s) {
 if (s != null) {
 if (s.length() == 0)
 return s;
 System.out.println(s);
 return semLetraMeio(new StringBuilder(s).deleteCharAt(s.length() % 2 == 0 ? s.length() / 2 - 1 : s.length() / 2).toString());
 }
 return s;
 }

– NinjaTroll
@I don’t see why recursion is better for this case. You may have created fewer variables, but it will take up more space in the stack with this bunch of recursive calls (maybe it’s even worse than "avoid variables") - and doing everything in a row just to avoid variables doesn’t always make the code "better". Variables are not an evil to be avoided, you use when it makes sense and it’s only bad to create variables that serve no purpose or make the code less clear. And I think it makes sense to create one for the index to be removed, for example, since it is the central point of the algorithm :-)
– hkotsubo
In this case:
static String semLetraMeio(String s) {
 if (s != null) {
 if (s.length() == 0)
 return s;
 while (s.length() > 0) {
 System.out.println(s);
 s = new StringBuilder(s)
 .deleteCharAt(s.length() % 2 == 0 ? s.length() / 2 - 1 : s.length() / 2)
 .toString());
 }
 return s;
 }
 return s;
}
– NinjaTroll
Only since strings are immutable, that would be better:
static String semLetraMeio(String s) {
 if (s != null) {
 if (s.length() == 0)
 return s;
 StringBuilder sb = new StringBuilder(s);
 while (sb.length() > 0) {
 System.out.println(sb);
 sb.deleteCharAt(sb.length() % 2 == 0 ? sb.length() / 2 - 1 : sb.length() / 2);
 }
 return sb.toString();
 }
 return s;
}
– NinjaTroll