This question already has answers here:
Check string for palindrome
(42 answers)
Closed 3 years ago.
I've been asked in class to determine whether a sentence string (ignoring the upper/lower cases, punctuations, and spaces) is a palindrome or not, and return true/false accordingly.
I've already had a look at several of the discussions here relating to palindrome strings, but I still can't seem to figure out what exactly is wrong with my code, especially because it works in some cases but not others.
In class sentence I have prive String mySentence and private int myNumWords. Regarding the static boolean isPalindrome, I was encouraged to try recursion but I thought to try iteration first.
//Constructor. Creates sentence from String str.
// Finds the number of words in sentence.
//Precondition: Words in str separated by exactly one blank.
public Sentence( String str )
{
mySentence = str;
int count = 0;
for(int k = 0; k<str.length(); k++)
{
if(str.substring(k,k+1).equals(" "))
count++;
}
myNumWords = count + 1;
}
public int getNumWords()
{
return myNumWords;
}
public String getSentence()
{
return mySentence;
}
//Returns copy of String s with all blanks removed.
//Postcondition: Returned string contains just one word.
private static String removeBlanks( String s )
{
return s.replaceAll(" ", "");
}
//Returns true if mySentence is a palindrome, false otherwise.
public boolean isPalindrome()
{
boolean palindrome = true;
String sentence = removeBlanks(mySentence);
String newsentence = removePunctuation(sentence);
int indice1 = 0;
int indice2 = newsentence.length() - 1;
while(indice1 < indice2)
{
if(newsentence.charAt(indice1)!= newsentence.charAt(indice2))
palindrome = false;
indice1++;
indice2--;
}
return palindrome;
}
//Precondition: s has no blanks, no punctuation, and is in lower case.
//Returns true if s is a palindrome, false otherwise.
private static boolean isPalindrome( String s, int start, int end )
{
boolean palindrome = true;
String sentence = removeBlanks(s);
String newsentence = removePunctuation(sentence);
int indice1 = 0;
int indice2 = newsentence.length() - 1;
while(indice1 < indice2)
{
if(newsentence.charAt(indice1)!= newsentence.charAt(indice2))
palindrome = false;
indice1++;
indice2--;
}
return palindrome;
}
//Returns copy of String s with all letters in lowercase.
//Postcondition: Number of words in returned string equals
// number of words in s.
private static String lowerCase( String s )
{
return s.toLowerCase();
}
//Returns copy of String s with all punctuation removed.
//Postcondition: Number of words in returned string equals
// number of words in s.
private static String removePunctuation( String s )
{
return s.replaceAll("\\p{Punct}", "");
}
For instance, for "A Santa lived as a devil at NASA" as my input, I'm supposed to get "true", but keep returning "false".
if you use new Sentence("A Santa lived as a devil at NASA").isPalindrome() then you foget do lowerCase
Related
This question already has answers here:
Check string for palindrome
(42 answers)
Closed 1 year ago.
I am trying to write a method to check if a given word is a palindrome, but as of now it does not work. I suspect the error lies within the if-statement and the fact that you don't compare objects such as strings with == but instead with equals, is that right? However Java does not allow me to write: if (firstHalf.charAt(i).equals(secondHalf.charAt(j))), so what can I do to make it work? Are there other errors in the code?
public static boolean isPalindrome(String string) {
String firstHalf = string.substring(0, string.length() / 2);
String secondHalf = string.substring(string.length() / 2, string.length());
for (int i = 0; i <= firstHalf.length(); i++) {
for (int j = secondHalf.length(); j <= 0; j--) {
if (firstHalf.charAt(i) == secondHalf.charAt(j)) {
return true;
}
}
}
return false;
}
Why not do it this way?
public static boolean isPalindrome(String string){
StringBuilder sb = new StringBuilder(string);
sb.reverse();
return sb.toString().equals(string);
}
Your character test was backwards. All of the comparisons have to be equal for the String to be a palindrome.
Also, splitting the String is unnecessary. You can logically split the String with two indices.
Try this code.
public static boolean isPalindrome(String string) {
int frontIndex = 0;
int backIndex = string.length() - 1;
while (frontIndex < backIndex) {
if (string.charAt(frontIndex++) != string.charAt(backIndex--)) {
return false;
}
}
return true;
}
you can make each character a String like so
String s1 = "" + c1;
and then compare them with .equals(s1, s2)
Or you can use the Character class which is a wrapper around primitive character.
That would also enable you to use c1.equals(c2)
How to check if some String contains a specific String like "ABC72961". So we search for String which starts with "ABC" following by 5 digits. I've implemented a algorithm but I want it with "matches" or somehow else and then check the speed of these two solutions.
You may want to use regex for this
^ABC[0-9]{5}$
^ : Beginning of the string
ABC : Matches ABC literally (case-sensitive)
[0-9]{5} : Matches 5x numbers from 0 to 9
$ : End of the string
And use String#matches to test it
Regex101
Example
String regex = "^ABC[0-9]{5}$";
String one = "ABC72961";
String two = "ABC2345";
String three = "AB12345";
String four = "ABABABAB";
System.out.println(one.matches(regex)); // true
System.out.println(two.matches(regex)); // false
System.out.println(three.matches(regex)); // false
System.out.println(four.matches(regex)); // false
EDIT
Seeing your comment, you want it to work for String one = "textABC72961text" also. For that to be possible, you should just erase ^ and $ that limit the String.
.*ABC[0-9]{5}.*
EDIT 2
Here is if you want to extract it
if (s.matches(".*ABC[0-9]{5}.*")) {
Matcher m = Pattern.compile("ABC[0-9]{5}").matcher(s);
m.find();
result = m.group();
}
str.contains("ABC72961");
Returns true if str contains the string. False if not.
public String getString() {
String str = extractString();
return str;
}
public boolean exists() {
return !getString().trim().equals("") ? false : true;
}
private List<Integer> getPositionsOfABC() {
List<Integer> positions = new ArrayList<>();
int index = text.indexOf("ABC");
while (index > 0) {
positions.add(index);
index = text.indexOf("ABC", index + 1);
}
return positions;
}
private static boolean isInteger(String str) {
boolean isValidInteger = false;
try {
Integer.parseInteger(str);
isValidInteger = true;
} catch (NumberFormatException ex) {
return isValidInteger;
}
return isValidInteger;
}
private String extractString() {
List<Integer> positions = getPositionsOfABC();
for (Integer position : positions) {
int index = position.intValue();
String substring = text.substring(index, index + LENGTH_OF_DIGITS);
String lastDigits = substring.substring(3, substring.length());
if (isInteger(lastDigits)) {
return substring;
}
}
return "";
}
Here's a simple code that checks whether a substring exists in a string without using library functions, regex or other complex data structures.
class SSC {
public static void main(String[] args) {
String main_str <-- MAIN STRING
String sub_str <-- SUBSTRING
String w; int flag=0;
for(int i=0;i<=main_str.length()-sub_str.length();i++){
w="";
for(int j=0;j<sub_str.length();j++){
w+=main_str.charAt(i+j);
}
if(w.equals(sub_str))
flag++;
}
if(flag>0)
System.out.print("exists "+flag+" times");
else
System.out.print("doesn't exist");
}
}
Hope this helps.
I think what you want to use is java.util.regex.Pattern.
Pattern p = Pattern.compile("ABC(\d*)");
Matcher m = p.matcher("ABC72961");
boolean b = m.matches();
or if it shall be exactly 5 digits after "ABC", you can use the regex ABC(\d{5})
https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#compile(java.lang.String)
Another solution would be:
String stringToTest = "ABC72961"
boolean b = stringToTest.contains("ABC");
http://www.tutorialspoint.com/java/lang/string_contains.htm
You can use the String indexOf command like this:
int result = someString.indexOf("ABC72961")
result will be -1 if there are no matches.
If there is a match, the result will be the index where the match starts.
Given two small String of different length and one full String. Check if the full String is merged from two small String.
Example1: Input - "beautiful", "bauful", "eti" Output - true
Example2: Input - "beautiful", "baufl", "eti" Output - false
That means the characters in small String 1 and small String 2 are in the same order as in full String.
Here is my code:
public class StringMerger {
public static boolean isMerge(String s, String part1, String part2) {
StringBuilder sb = new StringBuilder();
int minLength = Math.min(part1.length(), part2.length());
int maxLength = Math.max(part1.length(), part2.length());
for(int i = 0; i < minLength; i++){
sb.append(part1.charAt(i)).append(part2.charAt(i));
}
for(int i = minLength; i < maxLength; i++){
if(part1.length() >= part2.length()) sb.append(part1.charAt(i));
else sb.append(part2.charAt(i));
}
String temp = sb.toString();
return (temp.equalsIgnoreCase(s)) ? true : false;
}
}
But I have only solve with: Input - "beautiful", "batfl", "euiu" Output - true. This is only one of that' cases. So how to solve it?
Checkout the below solution, you are welcome to break it
Edit1 Fix bug: the bug is not due to the space, it is because when there is a character match in both small strings, the algorithm need to examine which character to take by checking both alternatives
Edit2 Reduce unnecessary depth first search to improve efficiency
public class StringMerger {
public static void main(String[] args) {
System.out.println(isMerge("beautiful", "bauful", "eti")); // true
System.out.println(isMerge("beautiful", "baufl", "eti")); // false
System.out.println(isMerge("bbbbb", "bbb", "bb")); // true
System.out.println(isMerge("xyzxyz", "xyz", "xyz")); // true
System.out.println(isMerge("xyzxyz", "xyz", "xyzd")); // false
System.out.println(isMerge("backstreetboy", "beetb", "ackstroy")); // true
System.out.println(isMerge("Can we merge it? Yes, we can!", "Cn w riYes n!", "aemege t? , weca")); //true
System.out.println(isMerge("beautifulxyzx", "baufulx", "etixyz")); // true
}
public static boolean isMerge(String s, String part1, String part2) {
if (s.length() != part1.length() + part2.length()) return false;
int part1Counter = 0;
int part2Counter = 0;
int fullStrLen = s.length();
int fullStrCounter = 0;
while (fullStrCounter < fullStrLen) {
boolean part1match = part1Counter < part1.length() && s.charAt(fullStrCounter) == part1.charAt(part1Counter);
boolean part2match = part2Counter < part2.length() && s.charAt(fullStrCounter) == part2.charAt(part2Counter);
if (part1match && part2match) { // if find a match in both substring, we need to find out whichever way works
boolean decision1 = isMerge(s.substring(fullStrCounter + 1), part1.substring(part1Counter + 1), part2.substring(part2Counter));
if (decision1) return decision1; // no need to check the second branch if the first branch matches
boolean decision2 = isMerge(s.substring(fullStrCounter + 1), part1.substring(part1Counter), part2.substring(part2Counter + 1));
return decision2;
}
if (part1match) {
++part1Counter;
++fullStrCounter;
} else if (part2match) {
++part2Counter;
++fullStrCounter;
} else {
return false;
}
}
return true;
}
}
Here's a shorter method. It makes use of Arrays.
private static boolean isMerge(String original, String one, String two){
String combinedStrings = one + two;
char[] mergedStrings = combinedStrings.toCharArray();
char[] originalString = original.toCharArray();
Arrays.sort(mergedStrings);
Arrays.sort(originalString);
return Arrays.equals(mergedStrings, originalString);
}
A possible brute-force starting algorithm, without giving you the solution for your homework:
Walk the word String looking for matches in each candidate String.
If there is no match (or both Strings are exhausted), then return False.
If there is a match, move the index for this candidate String.
If we exhaust the word String and all candidate Strings, return True.
I'm hand-waving past the edge conditions here, which would ideally be caught by tests.
It occurs to me that this class should be able to generate candidate Strings from any word String, meaning that you could even make the class symmetric.
I know I'm missing some things and that's what I really need help with. The code doesn't work in all cases and am looking for help improving/fixing it.
Assignment:
The code I have so far:
public String word(int num, String words)
{
int l = words.indexOf(" ");
int r = words.indexOf(" ", l+1);
for(int i = 3; i <= num; i++){
l = r;
r = words.indexOf(" ", l+1);
//if(i != num)
// l = r;
}
String theword = words.substring(l,r);
return theword;
}
}
As this is clearly homework, I will give you text only.
Your approach may work eventually, but it is laborious and overly complicated, so it's hard to debug and hard to get right.
make use of String's API by using the split() method
after splitting the sentence into an array of word Strings, return the element at num less one (array are indexed starting at zero
check the length of the array first, in case there are less words than num, and take whatever action you think is appropriate in that case
For part 2, a solution in a simple form may be:
create a new blank string for the result
iterate over the characters of the given string adding the character to the front of the result string
make use of String's toUpperCase() method
Since this is homework and you have showed some effort. This is how you can do part 1 of your question. This code is pretty evident.
1) I am returning null if number is greater than the number of words in string as we dont want user to enter 5 when there are only 2 words in a string
2) Splitting the string by space and basically returning the array with the number mentioned by user
There are more conditions which you must figure out such as telling the user to enter a number of the string length since it would not give him any result and taking input from Scanner instead of directy adding input in method.
public static String word(int num, String words)
{
String wordsArr[] = words.split(" ");
if(num <= 0 || num > wordsArr.length) return null;
return (wordsArr[num-1]);
}
the second part of your question must be attempted by you.
Well... not often you see people coming here with homework AND showing effort at the same time so bravo :).
This is example of how you can split the string and return the [x] element from that string
public class SO {
public static void main(String[] args) throws Exception {
int number = 3;
String word = "Hello this is sample code";
SO words = new SO();
words.returnWord(number, word);
}
private void returnWord(int number, String word) throws Exception {
String[] words = word.split("\\s+");
int numberOfWords = words.length;
if(numberOfWords >= number) {
System.out.println(words[number-1]);
} else {
throw new Exception("Not enought words!!!");
}
}
}
Yes it is a working example but do not just copy and paste that for your homework - as simple question from teacher - What is this doing, or how this works and your out :)! So understand the code, and try to modify it in a way that you are familiar what is doing what. Also its worth getting some Java book - and i recommend Head first Java by O'Really <- v.good beginner book!
if you have any questions please do ask!. Note that this answer is not 100% with what the textbook is asking for, so you can modify this code accordingly.
As of part 2. Well what Bohemian said will also do, but there is a lot quicker solution to this.
Look at StringBuilder(); there is a method on it that will be of your interest.
To convert String so all letter are upper case you can use .toUpperCase() method on this reversed string :)
You can try:
public class trial {
public static void main(String[] args)
{
System.out.println(specificword(0, "yours faithfully kyobe"));
System.out.println(reverseString("derrick"));}
public static String specificword(int number, String word){
//split by space
String [] parts = word.split("\\ ");
if(number <= parts.length){
return parts[number];
}
else{
return "null String";
}
}
public static String reverseString(String n){
String c ="";
for(int i = n.length()-1; i>=0; i--){
char m = n.charAt(i);
c = c + m;
}
String m = c.toUpperCase();
return m;
}
}
For the first problem, I'll give you two approaches (1. is recommended):
Use the String.split method to split the words up into an array of words, where each element is a word. Instead of one string containing all of the words, such as "hello my name is Michael", it will create an array of the words, like so [hello, my, name, is, Michael] and that way you can use the array to access the words. Very easy:
public static String word(int num, String words)
{
// split words string into array by the spaces
String[] wordArray = words.split(" "); // or = words.split("\\s+");
// if the number is within the range
if (num > 0 && num <= wordArray.length) {
return wordArray[num - 1]; // return the word from the word array
} else { // the number is not within the range of words
return null;
}
}
Only use this if you cannot use arrays! Loop through the word until you have found enough spaces to match the word you want to find:
public static String word(int num, String words)
{
for (int i = 0; i < words.length(); i++) { // every character in words
if (words.substring(i, i+1).equals(" ")) { // if word is a space
num = num - 1; // you've found the next word, so subtract 1 (number of words left is remaining)
}
if (num == 1) { // found all words
// return this word
int lastIndex = i+1;
while (lastIndex < words.length()) { // until end of words string
if (words.substring(lastIndex, lastIndex+1).equals(" ")) {
break;
}
lastIndex = lastIndex + 1; // not a space so keep moving along the word
}
/*
// or you could use this to find the last index:
int lastIndex = words.indexOf(" ", i + 1); // next space after i+1
if (lastIndex == -1) { // couldn't find another space
lastIndex = words.length(); // so just make it the last letter in words
}*/
if (words.substring(i, i+1).equals(" ")) { // not the first word
return words.substring(i+1, lastIndex);
} else {
return words.substring(i, lastIndex);
}
}
}
return null; // didn't find word
}
As for the second problem, just iterate backwards through the string and add each letter to a new string. You add each letter from the original string to a new string, but just back to front. And you can use String.toUpperCase() to convert the string to upper case. Something like this:
public static String reverse(String str) {
String reversedString = ""; // this will be the reversed string
// for every character started at the END of the string
for (int i = str.length() - 1; i > -1; i--) {
// add it to the reverse string
reversedString += str.substring(i, i+1);
}
return reversedString.toUpperCase(); // return it in upper case
}
I want to write a java method to return true if a string is a palindrome.
Here is what I have so far:
String palindrome = "...";
boolean isPalindrome = palindrome.equals(
new StringBuilder(palindrome).reverse().toString());
My problem with this is that it does not consider a word like: Race car to be a palindrome.
Doc, note, I dissent. A fast never prevents a fatness. I diet on cod.
What is the best way to test if this is a palindrome, with case insensitivity and ignoring punctuation.
Use this regex to remove all punctuation and spaces and convert it to lower case
String palindrome = "..." // from elsewhere
boolean isPalindrome = palindrome.replaceAll("[^A-Za-z]", "").toLowerCase().equals(new StringBuilder(palindrome.replaceAll("[^A-Za-z]", "").toLowerCase()).reverse().toString());
Try this ..
public static void main(String[] args) {
boolean notPalindrome = false;
String string = "Doc, note, I dissent. A fast never prevents a fatness. I diet on cod";
string = string.replaceAll("[^a-zA-Z]+","").toLowerCase();
char[] array = string.toCharArray();
for(int i=0, j=array.length-1; i<j; i++, j--) {
if(array[i] != array[j]) {
notPalindrome = true;
break;
}
}
System.out.println(string + " is palindrome? " + !notPalindrome);
}
Use the below regex, to keep even numeric characters in the Palindrome, if needed. Else, you can just remove the 0-9 from the regex.
String palindrome = "..." // from elsewhere
String regex = "[^A-Za-z0-9]";
boolean isPalindrome = palindrome.equals(new StringBuilder(palindrome.replaceAll(regex, "").toLowerCase()).reverse().toString());
Here is a non regex solution.
public class so4
{
public static void main(String args[])
{
String str = "Doc, note, I dissent. A fast never prevents a fatness. I diet on cod";
char c[] =str.toCharArray();
String newStr="";
for(int i=0;i<c.length;i++)
{
if( (c[i]>=65 && c[i]<=90) || (c[i]>=97 && c[i]<=122)) //check ASCII values (A-Z 65-90) and (a-z 97-122)
{
newStr = newStr + c[i];
}
}
boolean isPalindrome = newStr.toLowerCase().equals(new StringBuilder(newStr.toLowerCase()).reverse().toString());
System.out.println(isPalindrome);
}
}
convert to lower case
use a regex to remove everything but letters
reverse the string using a StringBuilder
compare the strings for equality
Code:
/**
* Returns true if s is a palindrome, ignoring whitespace
* punctuation, and capitalization. Returns false otherwise.
*/
public boolean isPalindrome(String s) {
String forward = s.toLowerCase().replaceAll("[^a-z]", "");
String reverse = new StringBuilder(forward).reverse().toString();
return forward.equals(reverse);
}
For more info, see the documentation for String and StringBuilder:
String.toLowerCase()
String.replaceAll()
StringBuilder.reverse()
You can also find it by googling "Java 7 String" and clicking the first result.