Making a Given String a Palindrome - java

Can someone please discuss and explain a way I can modify my code to function with these test cases... I am trying to make my program take a word and make it a palindrome by replacing one letter in a word that prevents the word from being a palindrome
Desired test cases:
Palindromes.isPalindrome2("cat", 'c') => true
Palindromes.isPalindrome2("axaa", 'x') => true
Palindromes.isPalindrome2("12bb", 'b') => true
Palindromes.isPalindrome2("ca", 'c') => true
This is what I have thus far...
public class Palindromes {
public static boolean isPalindrome(String word) {
//Strip out non-alphanumeric characters from string
String cleanWord = word.replaceAll("[^a-zA-Z0-9]","");
//Check for palindrome quality recursively
return checkPalindrome(cleanWord);
}
public static boolean isPalindrome2(String word) {
//Strip out non-alphanumeric characters from string
String cleanWord = word.replaceAll("[^a-zA-Z0-9]","");
//Check for palindrome quality recursively
return checkPalindrome2(cleanWord);
}
public static boolean checkPalindrome(String word) {
if(word.length() < 2) {
return true;
}
char first = word.charAt(0);
char last = word.charAt(word.length()-1);
if(first != last) {
return false;
}
else {
return checkPalindrome(word.substring(1,word.length()-1));
}
}
public void replace(int first, int last) {
if(first != last)
{ first = last;}
else if(last != first)
{ last = first;}
}
public static boolean checkPalindrome2(String word) {
char special = 0;
if(word.length() < 2) {
return true;
}
char first = word.charAt(0);
char last = word.charAt(word.length()-1);
if(first != last) {
return false;
}
if(first != last)
return false;
else {
return checkPalindrome2(word.substring(1,word.length()-1));
}
}
}
replace() was my attempt at handling the wildcard letter, but I cant seem to find the appropriate solution... All help will be greatly appreciated. thanks...

Here's my steps I would do:
Split the received string into 2 substrings. The first string front being the front half of the string, the second string back being the half end of the string.
Example:
char replacement = 'c';
String input = "aabbcc";
StringBuilder front = new StringBuilder(input.substring(0, input.length()/2));
// Do modulus to not include the odd middle (it mirrors itself)
StringBuilder back = new StringBuilder(input.substring((input.length()/2)+(input.length()%2));
Compare the two strings, replacing if one matches but the other doesn't. If neither match each other and is not the given 'replacement' character, return false. If you do more than one replacement, return false (since that is what you said the requirement is)
Example:
int replacements = 0;
for (int i=0; i < front.length(); ++i)
{
int backIndex = back.length() - i;
if (front.charAt(i) != back.charAt(backIndex))
{
// Characters do not match at all to given replacement
if ((front.charAt(i) != replacement) &&
(back.charAt(backIndex) != replacement)
{
// Cannot make it
// (Or if you want to force it, set both to replacement
// by deleting this one if statement)
return false;
}
// Front matches replacement
else if (front.charAt(i) == replacement)
{
// Replace back character with replacement
back.setCharAt(backIndex, replacement);
replacements++;
}
// Back matches replacement
else if (back.charAt(backIndex) == replacement)
{
// Replace front character with replacement
front.setCharAt(i, replacement);
replacements++;
}
if (replacements > 1)
{
// Can only replace one
return false;
}
}
}
String output = front.toString() + back.toString();

Here's my code, it splits the input into two halves, and compares the first half to the reversed second half. If they are equal, the input is already a palindrome. If they are not equal, it iterates through the first half, exchanging letters with the input char to replace with, and comparing with the reversed second half at every step. Then it does the same thing, but using the second half instead of the first half:
public class CanMakePalindrome {
public static void main(String[] args) {
System.out.println("cat using c: " + canMakePalindrome("cat", 'c'));
System.out.println("axaa using x: " + canMakePalindrome("axaa", 'x'));
System.out.println("12bb using b: " + canMakePalindrome("12bb", 'b'));
System.out.println("ca using c: " + canMakePalindrome("ca", 'c'));
}
private static boolean canMakePalindrome(String input, char c) {
int length = input.length();
String start = input.substring(0, length/2);
String end = input.substring(length/2+length%2, length); // need modulus in the case of odd length input
return (replaceLoop(start,end, c) || replaceLoop(end,start, c));
}
private static boolean replaceLoop(String start, String end, char c) {
if (start.equals(reverse(end))) {
System.out.println("Input is already a palindrome.");
return true;
}
for (int i=0; i<start.length(); i++) {
char[] startchars = start.toCharArray();
char[] endchars = end.toCharArray();
endchars = reverse(endchars);
startchars[i] = c;
if ((new String(startchars).equals(new String(endchars)))) return true;
}
return false;
}
private static char[] reverse(char[] input) {
int length = input.length;
char[] reversed = new char[length];
for (int i=0;i<length;i++) {
reversed[length-i-1]=input[i];
}
return reversed;
}
private static String reverse(String input){
String reversed = new String(reverse(input.toCharArray()));
return reversed;
}
}
Output:
cat using c: true
axaa using x: true
12bb using b: false
ca using c: true
Note that 12bb cannot be made into a palindrome using only one character change, so your test case appears to not match your specifications of replacing only one letter. Also my code will return true if given an empty string as input.

Related

How to check for palindrome excluding the non-alphanumeric characters?

Here's the code that I attempted
public String isPalindrome(String s) {
String trimmed = s.replaceAll("[^A-Za-z0-9]", "");
String reversed = "";
int len = trimmed.length();
for (int i = len - 1; i >= 0; i--) {
char[] allChars = trimmed.toCharArray();
reversed += allChars[i];
}
if (trimmed.equalsIgnoreCase(reversed)) {
return "true";
} else {
return "false";
}
}
Sample Input 1
A man, a plan, a canal: Panama
Sample Output 1
true
Explanation 1
The given string is palindrome when considering only alphanumeric characters.
Sample Input 2
race a car
Sample Output 2
false
Explanation 2
The given string is not a palindrome when considering alphanumeric characters.
Your variable len comes from the length of the String s. But you use the value on the array coming from trimmed.
So if you want to remove the IndexOutOfBoundsException you should change your len declaration to:
int len = trimmed.length();
You can return boolean instead of String:
public static boolean isPalindrome(String s) {
String trimmed = s.replaceAll("[^A-Za-z0-9]", "").toLowerCase();
int from = 0, to = trimmed.length() - 1;
while (from < to) {
if (trimmed.charAt(from) != trimmed.charAt(to)) {
return false;
}
from++;
to--;
}
return true;
}
You can use StringBuilder to reverse a String:
public static void main(String[] args) {
String input = "a#b!b^a";
String clean = input.replaceAll("[^A-Za-z0-9]", "");
String reverse = new StringBuilder(clean).reverse().toString();
boolean isPalindrome = reverse.equals(clean);
System.out.println(isPalindrome);
}
You can do like this in linear time as the loops are driven by the presence of non-alphabetic/digit characters. Also, no trimming or reversal of the string is required.
String[] test = {"A man, a plan, a canal: Panama",
"race a car","foobar", "ABC2CEc2cba"};
for (String s : test) {
System.out.printf("%5b -> %s%n", isPalindrome(s), s);
}
prints
true -> A man, a plan, a canal: Panama
false -> race a car
false -> foobar
true -> ABC2CEc2cba
The outer while loop drives then entire process until the indices cross or are equal. The inner loops simply skip over non-alphabetic/digit characters.
public static boolean isPalindrome(String s) {
int k = s.length() - 1;
int i = 0;
char c1 = '#';
char c2 = '#';
while (i <= k) {
while (!Character.isLetterOrDigit(c1 = s.charAt(i++)));
while (!Character.isLetterOrDigit(c2 = s.charAt(k--)));
if (Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
return false;
}
}
return true;
}

Find the amount of occurances of a character in a string recursively (java)

I am trying to write a method which returns the number of times the first character of a string appears throughout the string. This is what I have so far,
public int numberOfFirstChar0(String str) {
char ch = str.charAt(0);
if (str.equals("")) {
return 0;
}
if ((str.substring(0, 1).equals(ch))) {
return 1 + numberOfFirstChar0(str.substring(1));
}
return numberOfFirstChar0(str);
}
however, it does not seem to work (does not return the correct result of how many occurrences there are in the string). Is there anything wrong with the code? Any help is appreciated.
This uses 2 functions, one which is recursive. We obtain the character at the first index and the character array from the String once instead of doing it over and over and concatenating the String. We then use recursion to continue going through the indices of the character array.
Why you would do this I have no idea. A simple for-loop would achieve this in a much easier fashion.
private static int numberOfFirstChar0(String str) {
if (str.isEmpty()) {
return 0;
}
char[] characters = str.toCharArray();
char character = characters[0];
return occurrences(characters, character, 0, 0);
}
private static int occurrences(char[] characters, char character, int index, int occurrences) {
if (index >= characters.length - 1) {
return occurrences;
}
if (characters[index] == character) {
occurrences++;
}
return occurrences(characters, character, ++index, occurrences);
}
Java 8 Solution
private static long occurrencesOfFirst(String input) {
if (input.isEmpty()) {
return 0;
}
char characterAtIndexZero = input.charAt(0);
return input.chars()
.filter(character -> character == characterAtIndexZero)
.count();
}
Here is a simple example of what you are looking for.
Code
public static void main(String args[]) {
//the string we will use to count the occurence of the first character
String countMe = "abcaabbbdc";
//the counter used
int charCount=0;
for(int i = 0;i<countMe.length();i++) {
if(countMe.charAt(i)==countMe.charAt(0)) {
//add to counter
charCount++;
}
}
//print results
System.out.println("The character '"+countMe.charAt(0)+"' appears "+ charCount+ " times");
}
Output
The character 'a' appears 3 times

How to check that one String can be spelled using characters from another String?

Example: String a = "ACAHBBA" and String b = "ABAB" should return true, since both strings can spell ABAB.
I have tried with contains(), but that only works for equal sequences.
// The code should look like this.
public class task10 {
public static boolean contains(String a, String b) {
// check if b can be spelled using characters from a.
// if it can. return true.
// else
return false;
}
}
Posible solution?
public static boolean contains(String a, String b) {
for (int i = 0; i < b.length(); i++) {
if (a.indexOf(b.charAt(i)) == -1) {
return false;
}
}
return true;
}
Simply iterate thru one string and get the index of the character. If >= 0, replace character with non-alphabetic character and repeat. This algorithm presumes the need to match the correct number of characters. For example, hello would return false if the character set was helo.
public static boolean spelledFrom(String word, String chars) {
StringBuilder sb = new StringBuilder(chars);
for (String c : word.split("")) {
int i;
if ((i = sb.indexOf(c)) < 0) {
return false;
}
sb.setCharAt(i, '#');
}
return true;
}
You can try this:
public static boolean canSpell(String a, String b)
{
String shorter = (a.length() <= b.length()) ? a : b;
String longer = (shorter.equals(a)) ? b : a;
for(int i = 0; i < shorter.length(); i++)
{
if(!longer.contains("" + shorter.charAt(i)))
return false;
}
return true;
}
Once you've identified the shorter string, you just need to verify that each of its chars are contained in the longer string. This solution doesn't verify if a char "has already been used", which means inserting "AB" and "ABBA" will return true. If you need to do this, you just need to delete the verified char from the longer string in every loop.

How to write static recursive method in Java?

I want to write codes using a static recursive method in Java, cleanString(String s) that accepts a string of letters s and returns a string where adjacent letters that are the same are replaced by a single occurrence of that letter. The method is case-sensitive.
For example:
cleanString("sensssaatiionnaallll!") -> "sensational!"
cleanString("PPProoggggraamm") -> "Program"
cleanString("Lletterriiing") -> "Lletering"
Try this:
public class Main {
public static void main(String[] args) {
System.out.println(cleanString("sensssaatiionnaallll!"));
}
static String cleanString(String input)
{
if(input.length()<1) //To stop infinite recursion
return input;
var first = input.charAt(0);
var count = input.chars().takeWhile(x -> x == first).count();
return first + cleanString(input.substring((int)count));
}
}
First, it checks if the length of the string is less than 1. If it is, return the string itself (which is empty) and stop the recursion.
Next get the first character of the string. (e.g PPProoggggraamm -> P)
Get the number of characters in the start that equal the first character (3 in the case of PPProoggggraamm)
Call the function again, but this time lopping off the first n characters from the above step, and prepending the first character. ('P' + cleanString("rooggggraamm"))
Shortest recursive code to remove adjacent characters from the input string.
public class StackOverflow {
static String cleanString(String input) {
return input==null || input.length()<=1?input:cleanStringWrapper(input.substring(1),input.substring(0,1));
}
static String cleanStringWrapper(String input, String result) {
if (input.length() - 1 <= 0) {
return result+(result.charAt(result.length() - 1)!=input.charAt(0)?input:"");
} else {
return cleanStringWrapper(input.substring(1), result+(result.charAt(result.length() - 1) != input.charAt(0)?input.charAt(0):""));
}
}
public static void main(String[] args)
{
System.out.println(cleanString("OOPS"));
}
}
Output:
cleanString("sensssaatiionnaallll!") -> "sensational!"
cleanString("PPProoggggraamm") -> "Program"
cleanString("Lletterriiing") -> "Lletering"
cleanString("Gooooogle") -> "Gogle"
cleanString("ABC") -> "ABC"
cleanString("A") -> "A"
cleanString("") -> ""
cleanString(null) -> null
It just generate a new String and exclude the repeat characters.
static String cleanString(String input) {
if(input == null) return null;
char lastChar = 0;
StringBuilder output = new StringBuilder(input.length());
for (int i=0,n=input.length(); i<n; i++) {
char c = input.charAt(i);
if(c != lastChar) {
lastChar = c;
output.append(c);
}
}
return output.toString();
}
Recursive method:
public class Example {
public static int main(String[] args) {
String input = "sensssaatiionnaallll";
String output = cleanString(input, 0);
System.out.println(output); // print: sensational
return 0;
}
private static String cleanString(String input, int index) {
if(input == null) return "";
if(index >= input.length()) return "";
StringBuilder output = new StringBuilder();
char current = input.charAt(index);
int nextIndex = index + 1;
if(nextIndex >= input.length()) {
return output.append(current).toString();
}
char next = input.charAt(nextIndex);
if (current != next) {
output.append(current);
}
output.append(cleanString(input, nextIndex));
return output.toString();
}
}
Why you want to make static method for that?
Whay i understood is that you want to remove repeated characters from your input string.
You can do it below code as well.
StringBuilder sb = new StringBuilder();
str.chars().distinct().forEach(c -> sb.append((char) c));
If you want you can make a method of this 2 lines as a feature in your code.
Hope this helps!

Java: Palindrome Finding Taking Too Long; How to Optimize?

I've written a function to find whether a given string (stripped of spaces) is a palindrome. Unfortunately, it takes too long to run. Any ideas how I can make the below code run faster? (I'm timing out on LeetCode's Online Judge):
public class Solution {
public boolean checkIfPalindrome(String s) {
if (s.length() == 0 || s.length() == 1) {
return true;
}
//if first letter == last letter
char first = s.charAt(0);
char second = s.charAt(s.length() - 1);
if (first == second) {
String shorterString = s.substring(1, s.length() - 1);
return isPalindrome(shorterString);
} else {
return false;
}
}
public String onlyCharacters(String s) {
String toReturn = "";
for (Character c : s.toCharArray()) {
if (Character.isLetter(c)) {
toReturn += c;
}
}
return toReturn;
}
public boolean isPalindrome(String s) {
s = onlyCharacters(s);
return checkIfPalindrome(s);
}
}
This isn't the most optimal way of finding if a string is palindrome or not.
Just loop through n/2 iterations (where n is length of string) and check if character at position i is equal to character at position n-i
If the length of the string s is n then s will be a palindrome if
s[i]=s[n-1-i] for i in range [0,ceil(n/2)] // 0 based index
Code:
public static boolean checkIfPalindrome(String s) {
for(int i=0;i<s.length()/2;i++) {
if(s.charAt(i)!=s.charAt(s.length()-i-1)) {
return false;
}
}
return true;
}
It's an algorithm method called "divide and conquer". But in this case is just to make it n/2 instead of n.
Here is a suitable algorithm that might just help :
1.For i = 1 to n/2
2.If string[i] = string[n-1] then continue in the loop
3.Else break and return false
4.return true
If n is the length of the input string, your code takes O(n^2) operations. This may surprise you because there are no nested loops in your code, but both the substring method and the += operator for Strings require the creation of a new String, which requires copying its contents.
To see this in action, I have inserted
System.out.println(s);
into the isPalindrome() and checkIfPalindrome() methods, and invoked
isPalindrome("doc, note: i dissent. a fast never prevents a fatness. i diet on cod");
This produces the following output:
docnoteidissentafastneverpreventsafatnessidietoncod
ocnoteidissentafastneverpreventsafatnessidietonco
ocnoteidissentafastneverpreventsafatnessidietonco
cnoteidissentafastneverpreventsafatnessidietonc
cnoteidissentafastneverpreventsafatnessidietonc
noteidissentafastneverpreventsafatnessidieton
noteidissentafastneverpreventsafatnessidieton
oteidissentafastneverpreventsafatnessidieto
oteidissentafastneverpreventsafatnessidieto
teidissentafastneverpreventsafatnessidiet
teidissentafastneverpreventsafatnessidiet
eidissentafastneverpreventsafatnessidie
eidissentafastneverpreventsafatnessidie
idissentafastneverpreventsafatnessidi
idissentafastneverpreventsafatnessidi
dissentafastneverpreventsafatnessid
dissentafastneverpreventsafatnessid
issentafastneverpreventsafatnessi
issentafastneverpreventsafatnessi
ssentafastneverpreventsafatness
ssentafastneverpreventsafatness
sentafastneverpreventsafatnes
sentafastneverpreventsafatnes
entafastneverpreventsafatne
entafastneverpreventsafatne
ntafastneverpreventsafatn
ntafastneverpreventsafatn
tafastneverpreventsafat
tafastneverpreventsafat
afastneverpreventsafa
afastneverpreventsafa
fastneverpreventsaf
fastneverpreventsaf
astneverpreventsa
astneverpreventsa
stneverprevents
stneverprevents
tneverprevent
tneverprevent
neverpreven
neverpreven
everpreve
everpreve
verprev
verprev
erpre
erpre
rpr
rpr
p
p
That's quite a wall of text we are asking the computer to compute! We also see that every String is created twice. That's because you needlessly invoke onlyCharacters() in every iteration.
To avoid creating intermediary String instances, you can use a String Builder:
String onlyCharacters(String s) {
StringBuilder toReturn = new StringBuilder();
for (Character c : s.toCharArray()) {
if (Character.isLetter(c)) {
toReturn.append(c);
}
}
return toReturn.toString();
}
Also, it turns out a StringBuilder has a cool method called reverse(), so we can simplify your program to:
boolean isPalindrome(String s) {
StringBuilder letters = new StringBuilder();
for (Character c : s.toCharArray()) {
if (Character.isLetter(c)) {
letters.append(c);
}
}
StringBuilder reversedLetters = new StringBuilder(letters).reverse();
return onlyLetters.equals(reversedLetters);
}
This code only creates 2 StringBuilder objects rather than n Strings, and is therefore about n/2 times faster than your code.
I found this to be faster than any other answer so far:
public class Solution {
public boolean isPalindrome(String s) {
for (int low = 0, high = s.length() - 1;; low++, high--) {
char cLow = 0, cHigh = 0;
// Find the next acceptable character for the increasing index.
while (low < high && !Character.isLetterOrDigit(cLow = s.charAt(low))) {
low++;
}
// Find the previous acceptable character for the decreasing index.
while (low < high && !Character.isLetterOrDigit(cHigh = s.charAt(high))) {
high--;
}
if (low >= high) {
// All previous character comparisons succeeded and we have a palindrome.
return true;
}
if (Character.toUpperCase(cLow) != Character.toUpperCase(cHigh)) {
// This is not a palindrome.
return false;
}
}
}
}
You have only one object: your original String. Every character is tested until we get acceptable characters (Character.isLetter). Then only those are compared.
No temporary object, no superflous checks. Straight to the goal: it does one thing but does it well.
Note: this answers the actual Leetcode OJ answer by checking alphanumerics instead of only letters and by not caring about the case.
You may use this StringBuilder.reverse() to check Palindrome:
private boolean isPalindrome(String str) {
StringBuilder strBuilder = new StringBuilder(str);
return str.equals(strBuilder.reverse().toString());
}

Categories