String palindrome checker in java not working? - java

I am trying to write a program for these instructions:
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.
Note: For the purpose of this problem, we define empty String as valid palindrome.
https://leetcode.com/problems/valid-palindrome/
For some reason, the .reverse() in the last line doesn't reverse it. I've tried debugging by adding print statements and I see that the string DOES reverse earlier on. What is going on here? Please guide me!
public static boolean isPalindrome(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (Character.isLetter(s.charAt(i))) {
sb.append(s.charAt(i));
}
}
String x = sb.reverse().toString();
System.out.println(x);
if (sb.length() == 0)
return true;
else
return sb.toString().equals(x);
}

The problem is that reverse() changes the StringBuilder itself. So you are comparing the reverse, with the reverse again. Instead change your method to:
String x = sb.toString();
return sb.reverse().toString().equals(x);
Which will compare the StringBuilder before reversing, with the reversed StringBuilder. Note that the if(sb.length == 0) is not necessary, as if it is empty, sb.reverse().toString().equals(x) will still return true.
Also your search is case sensitive, when the problem statement says that it should match regardless of case. Change where you append to:
if (Character.isLetter(s.charAt(i))) {
sb.append(Character.toLowerCase(s.charAt(i)));
}
Also you can take advantage of replaceAll and toLowerCase() to shorten your method to:
public static boolean pali(String s) {
String copy = s.toLowerCase().replaceAll("[^a-z]", "");
return new StringBuilder(copy).reverse().toString().equals(copy);
}

Because after first invocation of sb.reverse() sb has changed it state.

Related

Comparing array items, index out of bound

I have a piece of code and I'm a bit confused how to deal with my issue so, please review method below. I was trying to search for a solution but unfortunately none of them fit my needs so I am looking for an advice here. The method is taking a String and removing duplicated characters so for example - input: ABBCDEF should return ABCDEF, but when entering i+1 in the last iteration I got IndexOutOfBound Exception, so I can iterate until string.length-1 but then I loose the last element, what is the SMARTEST solution in your opinion, thanks.
public String removeDuplicates(String source){
if(source.length() < 2){
return source;
}
StringBuilder noDuplicates = new StringBuilder();
char[] string = source.toCharArray();
for(int i = 0; i < string.length-1; i++){
if(string[i] != string[i+1]){
noDuplicates.append(string[i]);
}
}
return noDuplicates.toString();
}
You could do this like so: append the first character in source, and then only append subsequent characters if they are not equal to the previously-appended character.
if (source.isEmpty()) {
return source; // Or "", it doesn't really matter.
}
StringBuilder sb = new StringBuilder();
sb.append(source.charAt(0));
for (int i = 1; i < source.length(); ++i) {
char c = source.charAt(i);
if (c != sb.charAt(sb.length() - 1)) {
sb.append(c);
}
}
return sb.toString();
But if you wanted to do this more concisely, you could do it with regex:
return source.replaceAll("(.)\\1+", "$1");
You could simply append the last character after the loop:
public String removeDuplicates(String source){
...
noDuplicates.append(string[string.length - 1]);
return noDuplicates.toString();
}
You have a simple logic error:
You make your string to a char array.
That is fine, but the length property of any array will show you the
human way of counting if someting is in it.
If there is 1 element the length will be 1
2 -> 2
3 -> 3
etc.
You get the idea.
So when you go string[i + 1] you go one character to far.
You could just change the abort condition to
i < = string.length - 2
Or you could write a string iterator, to be able to access the next element, but
that seems like overkill for this example
This is just what LinkedHashSet was made for! Under the hood it's a HashSet with an iterator to keep track of insertion order, so you can remove duplicates by adding to the set, then reconstruct the string with guaranteed ordering.
public static String removeDuplicates(String source) {
Set<String> dupeSet = new LinkedHashSet<>();
for (Character v : source.toCharArray()) {
dupeSet.add(v.toString());
}
return String.join("", dupeSet);
}
If you wish to remove all repeating characters regardless of their position in the given String you might want to consider using the chars() method which provides a IntStream of the chars and that has the distinct() method to filter out repeating values. You can then put them back together with a StringBuilder like so:
public class RemoveDuplicatesTest {
public static void main(String[] args) {
String value = "ABBCDEFE";
System.out.println("No Duplicates: " + removeDuplicates(value));
}
public static String removeDuplicates(String value) {
StringBuilder result = new StringBuilder();
value.chars().distinct().forEach(c -> result.append((char) c));
return result.toString();
}
}

Palindrome Tester in Java

I have a code to test a string of characters to see if they are in palindrome form. I get an error at on the last line saying Stringbuilder cannot be converted to String. I don't understand if I am building a string, how is that not a string. Also, part of the assignment was to create our own stack class. Mine is called MyStack, but I am not sure I am calling it anywhere except when I make a new stack. How do I call MyStack instead of the library stack?
public boolean isPalindrome(String line) throws StackException {
MyStack Stack = new MyStack();
// initialize loop counters
int i = 0;
int n = line.length();
/* Push all char to a_stack */
while (i < line.length()){
char ch = line.charAt(i);
Stack.push(ch);
i++;
}
StringBuilder result = new StringBuilder();
while (!Stack.empty()){
result.append(Stack.pop());
}
result.toString();
return inputString.equalsIgnoreCase(result);
}
Convert StringBuilder to String
return inputString.equalsIgnoreCase(result.toString());
You may try this:
return inputString.equalsIgnoreCase(result.toString());
instead of
return inputString.equalsIgnoreCase(result);
ie, you have to convert StringBuilder to String
Since String.equalsIgnoreCase(String) takes a String, and not a StringBuilder, you need to convert the StringBuilder to a String for that equality check, also I think you meant line not inputString
return line.equalsIgnoreCase(result.toString());

Java codingbat help - withoutString

I'm using codingbat.com to get some java practice in. One of the String problems, 'withoutString' is as follows:
Given two strings, base and remove, return a version of the base string where all instances of the remove string have been removed (not case sensitive).
You may assume that the remove string is length 1 or more. Remove only non-overlapping instances, so with "xxx" removing "xx" leaves "x".
This problem can be found at: http://codingbat.com/prob/p192570
As you can see from the the dropbox-linked screenshot below, all of the runs pass except for three and a final one called "other tests." The thing is, even though they are marked as incorrect, my output matches exactly the expected output for the correct answer.
Here's a screenshot of my output:
And here's the code I'm using:
public String withoutString(String base, String remove) {
String result = "";
int i = 0;
for(; i < base.length()-remove.length();){
if(!(base.substring(i,i+remove.length()).equalsIgnoreCase(remove))){
result = result + base.substring(i,i+1);
i++;
}
else{
i = i + remove.length();
}
if(result.startsWith(" ")) result = result.substring(1);
if(result.endsWith(" ") && base.substring(i,i+1).equals(" ")) result = result.substring(0,result.length()-1);
}
if(base.length()-i <= remove.length() && !(base.substring(i).equalsIgnoreCase(remove))){
result = result + base.substring(i);
}
return result;
}
Your solution IS failing AND there is a display bug in coding bat.
The correct output should be:
withoutString("This is a FISH", "IS") -> "Th a FH"
Yours is:
withoutString("This is a FISH", "IS") -> "Th a FH"
Yours fails because it is removing spaces, but also, coding bat does not display the correct expected and run output string due to HTML removing extra spaces.
This recursive solution passes all tests:
public String withoutString(String base, String remove) {
int remIdx = base.toLowerCase().indexOf(remove.toLowerCase());
if (remIdx == -1)
return base;
return base.substring(0, remIdx ) +
withoutString(base.substring(remIdx + remove.length()) , remove);
}
Here is an example of an optimal iterative solution. It has more code than the recursive solution but is faster since far fewer function calls are made.
public String withoutString(String base, String remove) {
int remIdx = 0;
int remLen = remove.length();
remove = remove.toLowerCase();
while (true) {
remIdx = base.toLowerCase().indexOf(remove);
if (remIdx == -1)
break;
base = base.substring(0, remIdx) + base.substring(remIdx + remLen);
}
return base;
}
I just ran your code in an IDE. It compiles correctly and matches all tests shown on codingbat. There must be some bug with codingbat's test cases.
If you are curious, this problem can be solved with a single line of code:
public String withoutString(String base, String remove) {
return base.replaceAll("(?i)" + remove, ""); //String#replaceAll(String, String) with case insensitive regex.
}
Regex explaination:
The first argument taken by String#replaceAll(String, String) is what is known as a Regular Expression or "regex" for short.
Regex is a powerful tool to perform pattern matching within Strings. In this case, the regular expression being used is (assuming that remove is equal to IS):
(?i)IS
This particular expression has two parts: (?i) and IS.
IS matches the string "IS" exactly, nothing more, nothing less.
(?i) is simply a flag to tell the regex engine to ignore case.
With (?i)IS, all of: IS, Is, iS and is will be matched.
As an addition, this is (almost) equivalent to the regular expressions: (IS|Is|iS|is), (I|i)(S|s) and [Ii][Ss].
EDIT
Turns out that your output is not correct and is failing as expected. See: dansalmo's answer.
public String withoutString(String base, String remove) {
String temp = base.replaceAll(remove, "");
String temp2 = temp.replaceAll(remove.toLowerCase(), "");
return temp2.replaceAll(remove.toUpperCase(), "");
}
Please find below my solution
public String withoutString(String base, String remove) {
final int rLen=remove.length();
final int bLen=base.length();
String op="";
for(int i = 0; i < bLen;)
{
if(!(i + rLen > bLen) && base.substring(i, i + rLen).equalsIgnoreCase(remove))
{
i +=rLen;
continue;
}
op += base.substring(i, i + 1);
i++;
}
return op;
}
Something things go really weird on codingBat this is just one of them.
I am adding to a previous solution, but using a StringBuilder for better practice. Most credit goes to Anirudh.
public String withoutString(String base, String remove) {
//create a constant integer the size of remove.length();
final int rLen=remove.length();
//create a constant integer the size of base.length();
final int bLen=base.length();
//Create an empty string;
StringBuilder op = new StringBuilder();
//Create the for loop.
for(int i = 0; i < bLen;)
{
//if the remove string lenght we are looking for is not less than the base length
// and the base substring equals the remove string.
if(!(i + rLen > bLen) && base.substring(i, i + rLen).equalsIgnoreCase(remove))
{
//Increment by the remove length, and skip adding it to the string.
i +=rLen;
continue;
}
//else, we add the character at i to the string builder.
op.append(base.charAt(i));
//and increment by one.
i++;
}
//We return the string.
return op.toString();
}
Taylor's solution is the most efficient one, however I have another solution that is a naive one and it works.
public String withoutString(String base, String remove) {
String returnString = base;
while(returnString.toLowerCase().indexOf(remove.toLowerCase())!=-1){
int start = returnString.toLowerCase().indexOf(remove.toLowerCase());
int end = remove.length();
returnString = returnString.substring(0, start) + returnString.substring(start+end);
}
return returnString;
}
#Daemon
your code works. Thanks for the regex explanation. Though dansalmo pointed out that codingbat is displaying the intended output incorrectly, I through in some extra lines to your code to unnecessarily account for the double spaces with the following:
public String withoutString(String base, String remove){
String result = base.replaceAll("(?i)" + remove, "");
for(int i = 0; i < result.length()-1;){
if(result.substring(i,i+2).equals(" ")){
result = result.replace(result.substring(i,i+2), " ");
}
else i++;
}
if(result.startsWith(" ")) result = result.substring(1);
return result;
}
public String withoutString(String base, String remove){
return base.replace(remove,"");
}

Java check all characters in string are present in a given string array

I am attempting to create a method that checks every character in userInput to see if they are present in operatorsAndOperands. The issue is that tempbool is always false for all values.
import java.util.*;
public class stringCalculator
{
private String userInput = null;
String[] operatorsAndOperands = {"1","2","3","4","5","6","7","8","9","0","+","-","*","/"};
public stringCalculator(String newInput)
{
userInput = newInput;
}
public boolean checkInput()
{
boolean ifExists = true;
for(int i=0; i<userInput.length(); i++)
{
char currentChar = userInput.charAt(i);
boolean tempbool = Arrays.asList(operatorsAndOperands).contains(currentChar);
if (tempbool == false)
{
ifExists = false;
}
}
return ifExists;
}
}
This is because you have an array of string objects (which you later convert to a list of string objects), but you are checking a char for presence in that array.
Efficiency is also pretty poor here - converting a fixed array to a list on each iteration takes a lot of unnecessary CPU cycles.
A simple solution to this problem is to put all characters in a string, and then check each incoming character against that string:
if ("0123456789+-*/".indexOf(currentChar) >= 0) {
... // Good character
}
Another solution would be making a regex that allows only your characters to be specified, like this:
if (expr.replaceAll("[0-9+/*-]*", "").length() == 0) {
... // Expr contains only valid characters
}
Why don't you declare
String[] operatorsAndOperands = {"1","2","3","4","5","6","7","8","9","0","+","-","*","/"};
as a String, instead of an array of String. Then you can just use the contains method to check the characters against the valid operators.
Declare: char[] operatorsAndOperands; instead of: String[] operatorsAndOperands.
Or add this: String.valueOf(charToCompare) as the "contains" argument.
As has been pointed out, the issue is that you're checking for a char in a list of String objects, so you'll never find it.
You can make this check easier, though, by using a regular expression:
Pattern operatorsAndOperands = Pattern.compile("[0-9+\\-*/]");

Java: How do I check the if a char at the nth position of a string is a letter or if is a number?

Let's say I have this string: "abcd123fx".
Now, I want to make a method which checks (in order) if "a","b","c","d" etc is a number and if not, returns false. I don't know how to handle the n-th position of char and for each char, in order.
You can check if a character is a letter of number with teh Character class.
String text = ...
char ch = texct.charAt(nth);
if (Character.isLetter(ch)) {
// is a letter
} else if (Character.isDigit(ch)) {
// is a digit
}
Note: these method support characters in different blocks of the unicode. e.g. it will accept characters in Arabic or Korean.
Check the documentation. You can use charAt function.
if (Character.isLetter(yourString.charAt(index)))
// ... Letter
if (Character.isDigit(yourString.charAt(index)))
// ... Number
Check this page
Well there are a few ways you could do this. The simplest would probably be something along the lines of:
Character.isDigit(someString.charAt(x))
or a regex way would be someString.substring(x,x).matches("[0-9]")
To get the nth character of a string you should use charAt, the you should use the Charachter's isLetterOrDigit.
Usually, when you face these problems, you should search the javadoc looking for suitable methods.
Check out the Java tutorials on oracle.com for more information.
Specifically for this subject:
Characters, specifically the Character.isLetter(char ch) and Character.isDigit(char ch) methods
Strings and Manipulating Characters in a String, the simplest method is String.charAt(int index)
- As you have said that you are a newbie, i won't make this complicated using Regex, but will use inbuilt Java functionalities to answer this.
- First use subString() method to get the "abcd" part of the String, then use toCharArray() method to break the String into char elements, then use Character class's isDigit() method to know whether its a digit or not.
Eg:
public class T1 {
public static void main(String[] args){
String s = "abcd123fx";
String str = s.substring(0,4);
System.out.println(str);
char[] cArr = str.toCharArray();
for(char a :cArr){
if(Character.isDigit(a)){
System.out.println(a+" is a digit");
}else{
System.out.println(a+" is not a digit");
}
}
}
}
This might help you
public static boolean isNumeric(String str) {
return str.matches("-?\\d+(.\\d+)?");
}
public static void main(String[] args){
System.out.println(isNumeric("abcd123fx"));
}
If you have a numeric string it will return true else false
public static void main(String[] args){
System.out.println(checkNumber("123a44"));
}
public static boolean checkNumber(String s){
for(int i = 0; i < s.length(); i++){
if(Character.isDigit(s.charAt(i))){
continue;
}
else{
return false;
}
}
return true;
}
You can also have a look into the ASCII table
Depending on this you can write a method:
private boolean isNumber(char a) {
int i = a;
if(i >= 48 && i <=57)
return true;
else
return false;
}
// now you can look by a String
private void checkString() {
String x = "abcd123fx ";
for(char counter : x.toCharArray())
System.out.println(isNumber(counter));
}

Categories