I expect the User to provide a sentence.
And as an output, they will reverse a string with only the odd-length words reversed (i.e. even-length words should remain intact).
static String secretAgentII(String s) {
StringBuffer sb = new StringBuffer();
String[] newStr = s.split(" ");
String result = "";
for (int i = 0; i < newStr.length; i ) {
if (newStr[i].length() % 2 != 0) {
sb.append(newStr[i]).reverse();
result = sb " ";
}
result = newStr[i] " ";
}
return result;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
System.out.println(secretAgentII(s));
}
Input:
One two three Four
Expected Output:
enO owT eerhT Four
The actual Output:
enO One owtOne two eerhtenOtwo three Four
How can I fix that?
CodePudding user response:
I went ahead and wrote a method for what I think you are asking for.
public static String secretAgentII(String input){
StringBuilder returnValue = new StringBuilder();
input = input.replaceAll(" ", " ");
String[] tempArray = input.split(" ");
for (int i = 0; i < tempArray.length; i ) {
String currentString = tempArray[i];
if (currentString.length() % 2 == 1) {
char[] tempArrayOfStringChars = currentString.toCharArray();
for (int j = tempArrayOfStringChars.length - 1; j >= 0; j--) {
returnValue.append(tempArrayOfStringChars[j]);
}
} else {
returnValue.append(currentString);
}
if (i != tempArray.length - 1) { //This prevents a leading space at the end of your string
returnValue.append(' ');
}
}
return returnValue.toString();
}
From what I could tell, you only want the words of odd length to be reversed.
My sample input and output was as follows.
Input: One two three four five six seven eight nine ten
Output: enO owt eerht four five xis neves thgie nine net
CodePudding user response:
Your problem is that you add to result
the whole sb
, instead of just the current reverse word. Meaning you need to "reset" (create a new
) StringBurrer
for each iteration.
You're also missing the else
where you want to preserve the correct word's order
for (int i = 0; i < newStr.length; i ) {
if (newStr[i].length() % 2 == 1) {
StringBuffer sb = new StringBuffer();
sb.append(newStr[i]);
result = sb.reverse() " ";
}
else {
result = newStr[i] " ";
}
}
CodePudding user response:
In your method secretAgentII
the StringBuffer should have no other values so that it would not be concatenated to other strings.
I placed sb.replace(0, newStr[i].length(), newStr[i]).reverse();
inside the for loop so that it would replace the existing string in every use.
I also placed an else before the line result = newStr[i] " ";
for the original string doesn't need to be concatenated when it is reversed.
static String secretAgentII(String s) {
StringBuffer sb = new StringBuffer();
String[] newStr = s.split(" ");
String result = "";
for (int i = 0; i < newStr.length; i ) {
if (newStr[i].length() % 2 != 0) {
sb.replace(0, newStr[i].length(), newStr[i]).reverse();
result = sb " ";
} else
result = newStr[i] " ";
}
return result;
}
Input: One Two Three Four
Output: enO owT eerhT Four
note: you are using too many spaces, try researching Java conventions on writing code.
CodePudding user response:
StringBuilder
StringJoiner
Never use plain string concatenation the loop because in Java String
s are immutable. That means every s1 s2
produces new intermediate string, which don't need (since only the end result would be used). Concatenation in the loop effects performance and increase memory allocation.
Therefore, it's highly advisable to use StringBuilder
, or other built-in mechanisms like static method String.join()
, StringJoiner
or Collector joining()
when you need to combine multiple strings together.
To avoid bother about adding a white space after each word ourself, we can make use of the StringJoiner
. Through its parameterized constructor we can provide the required delimiter " "
.
That's how it might be implemented:
public static String reverseOdd1(String str) {
StringJoiner result = new StringJoiner(" ");
String[] words = str.split(" ");
for (String word : words) {
if (word.length() % 2 != 0) result.add(new StringBuilder(word).reverse());
else result.add(word);
}
return result.toString();
}
Note that it's not advisable to use StringBuffer
in single-threaded environment because its methods are synchronized to make it tread-safe (and synchronization doesn't come for free), and instead use it sibling StringBuilder
which has the same methods.
Here's a couple of more advanced solutions.
Stream IPA - Collectors.joining()
You can generate a Stream of words, revers odd-length words using map()
operation, and generate the resulting string using Collector joining()
.
public static String reverseOdd2(String str) {
return Arrays.stream(str.split(" "))
.map(s -> s.length() % 2 == 0 ? s : new StringBuilder(s).reverse().toString())
.collect(Collectors.joining(" "));
}
Regex - Matcher.replaceAll()
Another option is to use regular expressions.
To capture a separate word we can use the following pattern:
public static final Pattern WORD = Pattern.compile("\\b(\\w )\\b");
Where \\b
is a so-called boundary matcher denoting a word boundary (for more information, refer to the documentation).
We can create a Matcher
instance using this patter and the given string and make use of the Java 9 method Matcher.replaceAll()
to generate the resulting string.
public static String reverseOdd3(String str) {
return WORD.matcher(str).replaceAll(matchResult -> {
String group = matchResult.group(1);
return group.length() % 2 == 0 ? group : new StringBuilder(group).reverse().toString();
});
}
Usage Example
main
public static void main(String[] args) {
System.out.println(reverseOdd1("One Two Three Four"));
System.out.println(reverseOdd2("One Two Three Four"));
System.out.println(reverseOdd3("One Two Three Four"));
}
Output:
enO owT eerhT Four
enO owT eerhT Four
enO owT eerhT Four