Here is a description:
"Write a program that, given an input sentence, alternates the case of every alphabetic character, starting with uppercase. Spaces and non-alphabetical characters should be added to the final output as is, i.e. they should not be taken into account when alternating between upper/lowercase."
Here is what I've tried and does not work (System.out.println in main method should return correct sentence):
public class Main {
public static void main(String[] args) throws IOException {
InputStreamReader reader = new InputStreamReader(System.in, StandardCharsets.UTF_8);
BufferedReader in = new BufferedReader(reader);
String line;
while ((line = in.readLine()) != null) {
System.out.println(changeToUppercaseOrLowercase(countLettersWithSpaces(line), line));
}
}
private static int countLettersWithSpaces(String sentence) {
int count = 0;
for (int i = 0; i < sentence.length(); i ++)
{
char c = Character.toUpperCase(sentence.charAt(i));
if (c >= 'A' && c <= 'Z' || c == ' ' )
count ++;
}
return count;
}
private static String changeToUppercaseOrLowercase(int countLetters, String sentence) {
StringBuilder stringBuilder = new StringBuilder();
for(int i=0; i<countLetters; i++) {
if (!sentence.substring(i,i+1).equals(" ")) {
if ((i % 2) == 0) {
stringBuilder.append(sentence.substring(i,i+1).toUpperCase());
}
else {
stringBuilder.append(sentence.substring(i,i+1).toLowerCase());
}
}
if (sentence.substring(i,i+1).equals(" ")) {
stringBuilder.append(" ");
i++;
}
}
return stringBuilder.toString();
}
}
But tests says that:
Input data:
We are the world
Expected result:
We ArE tHe WoRlD
Result:
We Re He OrLd
How to solve that? Thank you in advance!
You can use Character.isAlphabetic and keep a counter that is incremented each time a letter is encountered.
public static String alternateCase(String str){
int count = 0;
StringBuilder sb = new StringBuilder(str.length());
for(int i = 0; i < str.length(); i++){
char c = str.charAt(i);
if(Character.isAlphabetic(c))
sb.append(++count % 2 == 1 ? Character.toUpperCase(c) : Character.toLowerCase(c));
else sb.append(c);
}
return sb.toString();
}
use Character.isLetter() function to check if it's a letter or not. half your problem will be solved.
and your problem description and test case doesnt go with each other. Please try to clarify more.
There are many ways to fix this. This one has minimal impact on your existing code.
Use an evenOdd counter to ensure you are not skipping over characters but still maintaining the alternation.
private static String changeToUppercaseOrLowercase(int countLetters, String sentence) {
StringBuilder stringBuilder = new StringBuilder();
int evenOdd = 0; // init ********HERE*******
for(int i=0; i<countLetters; i++) {
if (!sentence.substring(i,i+1).equals(" ")) {
if ((evenOdd % 2) == 0) { // check ********HERE*******
stringBuilder.append(sentence.substring(i,i+1).toUpperCase());
}
else {
stringBuilder.append(sentence.substring(i,i+1).toLowerCase());
}
}
if (sentence.substring(i,i+1).equals(" ")) {
stringBuilder.append(" ");
evenOdd--; // adjust to preserve proper alternation ********HERE*********
}
evenOdd++; // the normal update ********HERE*******
}
return stringBuilder.toString();
}
Related
I've recently started java and I want to compress a string like this:
Input:aaaaabbbbwwwccc Output:a5b4w3c3
Input:aaabbccds Output:a3b2c2ds
Input:Abcd Output:Abcd
The following code is what I have done but, it does not work.
public class CompressString {
public static void main(String[] args) {
String out = "";
Scanner in = new Scanner(System.in);
String input = in.next();
int length = input.length();
int counter = 1;
if (length == 0) {
System.out.println(" ");
} else {
for (int i = 0; i<length;i++){
if (input.charAt(i)==input.charAt(i+1)){
counter++;
}else {
if (counter == 1){
out = out+input.charAt(i-counter);
}else{
out = out+input.charAt(i-counter)+counter;
}
}
i++;
counter = 1;
}
System.out.println(out.toString());
}
}
}
The simplest program to do that would loop through each character in the string and check when the character is different from the previous seen one and, if so, add the last one and its count to the compressed string:
String input = "aaaaabbbbwwwccc";
StringBuilder compressed = new StringBuilder();
char last = 0;
int lastCount = 0;
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (last == 0 || c != last) {
if (lastCount != 0) {
compressed.append(last);
if (lastCount > 1) {
compressed.append(lastCount);
}
}
last = c;
lastCount = 1;
} else {
lastCount++;
}
}
// take care of the last repeating sequence if any
if (lastCount > 0) {
compressed.append(last);
if (lastCount > 1) {
compressed.append(lastCount);
}
}
Here is a very compact way of doing this with a regex matcher along with a string buffer:
String input = "aaaaabbbbwwwccc";
Pattern r = Pattern.compile("(.)\\1{0,}");
Matcher m = r.matcher(input);
StringBuffer buffer = new StringBuffer();
while (m.find()) {
m.appendReplacement(buffer, m.group(1) + m.group(0).length());
}
m.appendTail(buffer);
System.out.println(buffer.toString());
This prints:
a5b4w3c3
For an explanation, the above logic searches for the regex pattern (.)\1{0,}. This will match any single character, along with that same character occurring again possibly one or more times afterwards. It then replaces with just the single character followed by the count of the number of times it occurs.
The purpose of this method is replace all but the first and last letters of each word with "_". I'm a complete novice when it comes to coding, so I'm certain my code is fairly incorrect. I think where my code starts functioning improperly is with the while loop.
EDIT: How do I make this method without using arrays or extra methods, like the split method?
public static String blankWords(String s1) {
StringBuilder sb = new StringBuilder();
if(s1.length() > 2) {
sb.append(s1.charAt(0));
for(int x = 1; x < s1.length() - 1; x = x + 1) {
char y = ' ';
while(y != s1.charAt(x)) {
sb.append("_");
x = x + 1;
}
}
sb.append(s1.charAt(s1.length() - 1));
return sb.toString();
}
return s1;
}
What my code is outputting:
HW2.blankWords("This is a Test.")
java.lang.StringIndexOutOfBoundsException: String index out of range: 15
at java.lang.String.charAt(Unknown Source)
at HW2.blankWords(HW2.java:73)
What my code should output:
HW2.blankWords("This is a Test.")
"T__s is a T__t."
Here is a pretty simple solution:
class Scratch {
public static void main(String[] args) {
System.out.println(blankWords("My name is sam orozco"));
}
public static String delim = "_";
public static String blankWords(String s1) {
// this split arg on one or more space
String[] words = s1.split("\\s+");
StringBuilder response = new StringBuilder();
for (String val : words) {
val = convertWord(val);
response.append(val).append(" ");
}
return response.toString().trim();
}
public static String convertWord(String val) {
int len = val.length();
StringBuilder bldr = new StringBuilder();
int index = 0;
for (char ch : val.toCharArray()) {
if (index == 0 || index == len - 1) {
bldr.append(ch);
} else {
bldr.append(delim);
}
index++;
}
return bldr.toString();
}
}
You can do this using a StringTokenizer that will extract words based on a list of delimiters. Since you want to keep those delimiters in the output, you'll instruct the tokenizer to return them as tokens:
String blankWords(String s) {
// build a tokenizer for your string, listing all special chars as delimiters. The last argument says that delimiters are going to be returned as tokens themselves (so we can include them in the output string)
StringTokenizer tokenizer = new StringTokenizer(s, " .,;:?!()[]{}", true);
// a helper class to build the output string; think of it as just a more efficient concat utility
StringBuilder sb = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
String blankWord = blank(tokenizer.nextToken());
sb.append(blankWord);
}
return sb.toString();
}
/**
* Replaces all but the first and last characters in a string with '_'
*/
private String blank(String word) {
// strings of up to two chars will be returned as such
// delimiters will always fall into this category, as they are always single characters
if (word.length() <= 2) {
return word;
}
// no need to iterate through all chars, we'll just get the array
final char[] chars = word.toCharArray();
// fill the array of chars with '_', starting with position 1 (the second char) up to the last char (exclusive, i.e. last-but-one)
Arrays.fill(chars, 1, chars.length - 1, '_');
// build the resulting word based on the modified array of chars
return new String(chars);
}
Here is the contents of a test that validates this implementation, using TestNG:
#Test(dataProvider = "texts")
public void testBlankWords(String input, String expectedOutput) {
assertEquals(blankWords(input), expectedOutput);
}
#DataProvider
public Object[][] texts() {
return new Object[][] {
{"This is a test.", "T__s is a t__t."},
{"This one, again, is (yet another) test!", "T__s o_e, a___n, is (y_t a_____r) t__t!"}
};
}
The main drawback of this implementation is that StringTokenizer requires you to list all the delimiters by hand. With a more advanced implementation, you can consider a delimiter any character that returns false for Character.isAlphabetic(c) or however you decide to define your non-word chars.
P.S.
This could be a "more advanced implementation", as I mentioned above:
static String blankWords(String text) {
final char[] textChars = text.toCharArray();
int wordStart = -1; // keep track of the current word start position, -1 means no current word
for (int i = 0; i < textChars.length; i++) {
if (!Character.isAlphabetic(textChars[i])) {
if (wordStart >= 0) {
for (int j = wordStart + 1; j < i - 1; j++) {
textChars[j] = '_';
}
}
wordStart = -1; // reset the current word to none
} else if (wordStart == -1) {
wordStart = i; // alphabetic characters start a new word, when there's none started already
} else if (i == textChars.length - 1) { // if the last character is aplhabetic
for (int j = wordStart + 1; j < i; j++) {
textChars[j] = '_';
}
}
}
return new String(textChars);
}
No while loop necessary!
Look ahead by 1 character to see if it's a space, or if the current character is a space, in that case you append it. Otherwise you make sure to add the next character (skipNext false).
Always add the last character
public static String blankWords(String s1) {
StringBuilder sb = new StringBuilder();
if(s1.length() > 2) {
Boolean skipNext = false;
for(int x = 0; x < s1.length() - 1; x = x + 1) {
if(s1.charAt(x) == ' ' || s1.charAt(x + 1) == ' ') {
sb.append(s1.charAt(x));
skipNext = false;
}
else {
if(skipNext) {
sb.append('_');
}
else {
sb.append(s1.charAt(x));
skipNext = true;
}
}
}
sb.append(s1.charAt(s1.length() - 1));
return sb.toString();
}
return s1;
}
For the more advanced programmer, use regular expression.
public static String blankWords(String s1) {
return s1.replaceAll("\\B\\w\\B", "_");
}
This correctly keeps the final t, i.e. blankWords("This is a Test.") returns "T__s is a T__t.".
I need to double each letter in a string using a for loop and an if-then statement. How can you comb through a string and test if each character is a letter or a symbol like an exclamation point? And then print the string back out with each letter doubled and each exclamation point tripled.
This is what I have so far. It's unfinished and it doesn't work at all, but am I on the right track?
import java.util.Scanner;
public class DoubleLetters{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("Enter a sentence:");
String sentence = scan.nextLine();
boolean isLetter = false;
for (int i = 0; i < sentence.length(); i++){
isLetter = Character.isLetter(sentence.charAt(i));
if (i == sentence.length() || sentence.charAt(i) == ' ' || isLetter == false){
System.out.print(sentence.charAt(i) + sentence.charAt(i));
}
}
It looks like you were on the right way, then passed the right exit and carried on the wrong way.
for (int i = 0; i < sentence.length(); i++){ [...] } is a right way to iterate over a string's characters.
Character.isLetter(c) is a right way to check whether a character is a letter.
However, your condition is chaotic :
why would you make special conditions for spaces and end characters?
why is your isLetter condition negated?
I think your condition should simply be
if (isLetter) { /* print twice */ }
else if (isExclamationPoint) { /* print "thrice" */ }
else { /* print once */ }
Try this:
import java.util.*;
public class DoubleLetters{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("Enter a sentence:");
String sentence = scan.nextLine();
StringBuilder sb = new StringBuilder();
for (Character c: sentence.toCharArray()){
sb.append(c);
if(Character.isLetter(c)){
sb.append(c);
}
else if(c == '!'){
sb.append(c).append(c);
}
}
sentence = sb.toString();
System.out.println(sentence);
}
}
When manipulating strings like this, it is best to use StringBuilder, which allocates a contiguous character buffer of a given size. You can count how big your output String needs to be, and pass this size to the StringBuffer on construction.
I would also recommend continuing to call String.charAt for maximum efficiency.
You may also want to encapsulate your routine in a function. You can take the input as a CharSequence for maximum utility.
public class DoubleLetters {
private static int getRepetitionCount(char c) {
if (Character.isLetter(c)) {
return 2;
} else if (c == '!') {
return 3;
} else {
return 1;
}
}
public static String doubleLetters(CharSequence in) {
int inLength = in.length();
int outLength = 0;
for (int i = 0; i < inLength; ++i) {
outLength += getRepetitionCount(in.charAt(i));
}
StringBuilder out = new StringBuilder(outLength);
for (int i = 0; i < inLength; ++i) {
char c = in.charAt(i);
int reps = getRepetitionCount(c);
for (int r = 0; r < reps; ++r) {
out.append(c);
}
}
return out.toString();
}
public static void main(String[] args) {
String test = "hello! world!";
System.out.print(doubleLetters(test));
}
}
In this specific case, you could alternatively allocate a buffer of size 3 * inLength, which will be large enough to hold any potential output string.
Please help me to identify my mistakes in this code. I am new to Java. Excuse me if I have done any mistake. This is one of codingbat java questions. I am getting Timed Out error message for some inputs like "xxxyakyyyakzzz". For some inputs like "yakpak" and "pakyak" this code is working fine.
Question:
Suppose the string "yak" is unlucky. Given a string, return a version where all the "yak" are removed, but the "a" can be any char. The "yak" strings will not overlap.
public String stringYak(String str) {
String result = "";
int yakIndex = str.indexOf("yak");
if (yakIndex == -1)
return str; //there is no yak
//there is at least one yak
//if there are yaks store their indexes in the arraylist
ArrayList<Integer> yakArray = new ArrayList<Integer>();
int length = str.length();
yakIndex = 0;
while (yakIndex < length - 3) {
yakIndex = str.indexOf("yak", yakIndex);
yakArray.add(yakIndex);
yakIndex += 3;
}//all the yak indexes are stored in the arraylist
//iterate through the arraylist. skip the yaks and get non-yak substrings
for(int i = 0; i < length; i++) {
if (yakArray.contains(i))
i = i + 2;
else
result = result + str.charAt(i);
}
return result;
}
Shouldn't you be looking for any three character sequence starting with a 'y' and ending with a 'k'? Like so?
public static String stringYak(String str) {
char[] chars = (str != null) ? str.toCharArray()
: new char[] {};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < chars.length; i++) {
if (chars[i] == 'y' && chars[i + 2] == 'k') { // if we have 'y' and two away is 'k'
// then it's unlucky...
i += 2;
continue; //skip the statement sb.append
} //do not append any pattern like y1k or yak etc
sb.append(chars[i]);
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(stringYak("1yik2yak3yuk4")); // Remove the "unlucky" strings
// The result will be 1234.
}
It looks like your programming assignment. You need to use regular expressions.
Look at http://www.vogella.com/articles/JavaRegularExpressions/article.html#regex for more information.
Remember, that you can not use contains. Your code maybe something like
result = str.removeall("y\wk")
you can try this
public static String stringYak(String str) {
for (int i = 0; i < str.length(); i++) {
if(str.charAt(i)=='y'){
str=str.replace("yak", "");
}
}
return str;
}
I'm dealing with logical expressions in strings. So far I have worked on the following method.
public static String modify(String expression)
{
String temp = expression;
String validated = "";
for(int idx=0; idx<temp.length(); idx++)
{
if(idx!=temp.length()-1)
{
if((Character.isAlphabetic(temp.charAt(idx))) && (Character.isAlphabetic(temp.charAt(idx+1))))
{
validated+=temp.substring(idx,idx+1);
validated+="*";
}
else
validated+=temp.substring(idx,idx+1);
}
else
validated+=temp.substring(idx);
}
return validated;
}
The following are examples of supposed input/output
input: AB+BC+ABC / output: (A*B)+(B*C)+(A*B*C)
input: (A+B)+ABC / output: (A+B)+(A*B*C)
input: (A+B)*(B+C)*(AB) / output: (A+B)*(B+C)*(A*B)
One way you can do it is simply keeping track of brackets with a boolean semaphore
public static String modify(String expression)
{
String temp = expression;
StringBuilder validated = new StringBuilder();
boolean inBrackets=false;
for(int idx=0; idx<temp.length()-1; idx++)
{
if((Character.isLetter(temp.charAt(idx))) && (Character.isLetter(temp.charAt(idx+1))))
{
if(!inBrackets){
inBrackets = true;
validated.append("(");
}
validated.append(temp.substring(idx,idx+1));
validated.append("*");
}
else{
validated.append(temp.substring(idx,idx+1));
if(inBrackets){
validated.append(")");
inBrackets=false;
}
}
}
validated.append(temp.substring(temp.length()-1));
if(inBrackets){
validated.append(")");
inBrackets=false;
}
return validated.toString();
}
Also never use string concatenation instead use StringBuilder or its predecessor StringBuffer in case you are seeking thread safe solution.
Here is what I would do, using StringBuilder and a split:
public static String modify(String expression)
{
StringBuilder finalString = new StringBuilder();
String[] subExpressions = expression.split("\\+");
List<String> formattedSubExpressions = new ArrayList<String>();
for (String subExpression : subExpressions) {
if (subExpression.length() > 1) {
StringBuilder formattedSubExpression = new StringBuilder();
formattedSubExpression.append("(");
for (int i=0; i<subExpression.length(); i++) {
formattedSubExpression.append(subExpression.charAt(i));
if (i != subExpression.length() -1 ) {
formattedSubExpression.append("*");
}
}
formattedSubExpression.append(")");
formattedSubExpressions.add(formattedSubExpression.toString());
} else {
formattedSubExpressions.add(subExpression);
}
}
for (String subExpression : formattedSubExpressions) {
finalString.append(subExpression);
finalString.append("+");
}
if (finalString.charAt(finalString.length() - 1) == '+') {
finalString.deleteCharAt(finalString.length() - 1);
}
return finalString.toString();
}
It gives the following sample input/output:
AB+CD: (A*B)+(C*D)
AB+CD+EF: (A*B)+(C*D)+(E*F)
AB+CD+EFGH: (A*B)+(C*D)+(E*F*G*H)
I based this answer on the idea that what you want to do is group repeating alpha characters between parentheses and put an asterisks between them regardless of the operation (add, subtract, divide, etc) being performed between the groups.
private static final Pattern p = Pattern.compile("[a-zA-Z]{2,}");
public String parse(String s){
if(s == null || "".equals(s)) {
return s;
}
char[] chars = s.toCharArray();
StringBuilder sb = new StringBuilder(100);
Matcher m = p.matcher(s);
int i = 0;
while(i<chars.length && m.find()){
int startIdx = m.start();
int endIdx = m.end();
// Need to get the leading part of the string before this matching region
while(i < startIdx){
sb.append(chars[i]);
i++;
}
sb.append('('); // Start getting the match region
while(i < endIdx){
sb.append(chars[i]);
if(i < endIdx - 1){
sb.append('*');
}
i++;
}
sb.append(')'); // end the match region
}
// If there is a region beyond the last match, append it
if(i < chars.length -1){
for(; i < chars.length; i++){
sb.append(chars[i]);
}
}
return sb.toString();
}