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;
}
Related
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.".
Here is my code for whether two strings are anagrams or not
static boolean isAnagram(String a, String b) {
if (a.length() != b.length()) return false;
a = a.toLowerCase();
b = b.toLowerCase();
int m1=0;
for(int i=0;i<a.length();i++){
m1 += (int)a.charAt(i);
m1 -= (int)b.charAt(i);
}
return m1==0;
}
My code fails for two test cases
case 1: String a="xyzw";and String b="xyxy";
case 2: String a="bbcc"; and String b="dabc";
can anyone help me passing the above two cases?
I think your code doesn't work because you sum up the code of characters but maybe answer is zero however their are not equal, for example: "ad" "bc"
the better way is to do this is to sort characters of strings, if they has same array length and same order, so two string are anagram.
static boolean isAnagram(String str1, String str2) {
int[] str1Chars = str1.toLowerCase().chars().sorted().toArray();
int[] str2Chars = str2.toLowerCase().chars().sorted().toArray();
return Arrays.equals(str1Chars, str2Chars);
}
I hope this help you. (it is a little hard because I use stream to create and sort array of characters)
Try this:
import java.io.*;
class GFG{
/* function to check whether two strings are
anagram of each other */
static boolean areAnagram(char[] str1, char[] str2)
{
// Get lenghts of both strings
int n1 = str1.length;
int n2 = str2.length;
// If length of both strings is not same,
// then they cannot be anagram
if (n1 != n2)
return false;
// Sort both strings
quickSort(str1, 0, n1 - 1);
quickSort(str2, 0, n2 - 1);
// Compare sorted strings
for (int i = 0; i < n1; i++)
if (str1[i] != str2[i])
return false;
return true;
}
// Following functions (exchange and partition
// are needed for quickSort)
static void exchange(char A[],int a, int b)
{
char temp;
temp = A[a];
A[a] = A[b];
A[b] = temp;
}
static int partition(char A[], int si, int ei)
{
char x = A[ei];
int i = (si - 1);
int j;
for (j = si; j <= ei - 1; j++)
{
if(A[j] <= x)
{
i++;
exchange(A, i, j);
}
}
exchange (A, i+1 , ei);
return (i + 1);
}
/* Implementation of Quick Sort
A[] --> Array to be sorted
si --> Starting index
ei --> Ending index
*/
static void quickSort(char A[], int si, int ei)
{
int pi; /* Partitioning index */
if(si < ei)
{
pi = partition(A, si, ei);
quickSort(A, si, pi - 1);
quickSort(A, pi + 1, ei);
}
}
/* Driver program to test to print printDups*/
public static void main(String args[])
{
char str1[] = {'t','e','s','t'};
char str2[] = {'t','t','e','w'};
if (areAnagram(str1, str2))
System.out.println("The two strings are"+
" anagram of each other");
else
System.out.println("The two strings are not"+
" anagram of each other");
}
}
The implementation isn't correct. While a pair of anagrams will always have the same length and the same sum of characters, this is not a sufficient condition. There are many pairs of strings that have the same length and the same sum of characters and are not anagrams. E.g., "ad" and "bc".
A better implementation would count the number of times each character appears in each string and compare them. E.g.:
public static boolean isAnagram(String a, String b) {
return charCounts(a).equals(charCounts(b));
}
private static Map<Integer, Long> charCounts(String s) {
return s.chars()
.boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
static boolean isAnagram(String a, String b) {
if (a.length() != b.length())
return false;
a = a.toLowerCase();
b = b.toLowerCase();
HashMap<Integer, Integer> m1 = new HashMap<>(); // Key is ascii number, Value is count. For String a
HashMap<Integer, Integer> m2 = new HashMap<>(); // Key is ascii number, Value is count. For String b
for (int i = 0; i < a.length(); i++) {
int an = (int) (a.charAt(i));
int bn = (int) (b.charAt(i));
// Add 1 to current ascii number. String a.
if (m1.containsKey(an)) {
m1.put(an, m1.get(an) + 1);
}else {
m1.put(an, 1);
}
// Add 1 to current ascii number. String b.
if (m2.containsKey(bn)) {
m2.put(bn, m2.get(bn) + 1);
}else {
m2.put(bn, 1);
}
}
//Check both count equals().
return m1.equals(m2);
}
you should check per every letters.
If (ascii of a[0] == ascii of b[0] + 1) and (ascii of a[1] == ascii of b[1] - 1) It will return true because 1 - 1 is zero.
Sorry for very very complex code.
Adding character values is error prone logic, because A+C and B+B generate same number. The best option with this case is using Arrays. Look at the code below -
static boolean isAnagram(String a, String b) {
if (a.length() != b.length()) return false;
a = a.toLowerCase();
b = b.toLowerCase();
char[] charA = a.toCharArray();
Arrays.sort(charA);
char[] charB = b.toCharArray();
Arrays.sort(charB);
return Arrays.equals(charA, charB);
}
This should give you what you want.
Try this. It will execute in the O(word.length).
public boolean checkForAnagram(String str1, String str2) {
if (str1 == null || str2 == null || str1.length() != str2.length()) {
return false;
}
return Arrays.equals(getCharFrequencyTable(str1), getCharFrequencyTable(str2));
}
private int[] getCharFrequencyTable(String str) {
int[] frequencyTable = new int[256]; //I am using array instead of hashmap to make you realize that its a constant time operation.
char[] charArrayOfStr = str.toLowerCase().toCharArray();
for(char c : charArrayOfStr) {
frequencyTable[c] = frequencyTable[c]+1;
}
return frequencyTable;
}
Check out below methods :
/**
* Java program - String Anagram Example.
* This program checks if two Strings are anagrams or not
*/
public class AnagramCheck {
/*
* One way to find if two Strings are anagram in Java. This method
* assumes both arguments are not null and in lowercase.
*
* #return true, if both String are anagram
*/
public static boolean isAnagram(String word, String anagram){
if(word.length() != anagram.length()){
return false;
}
char[] chars = word.toCharArray();
for(char c : chars){
int index = anagram.indexOf(c);
if(index != -1){
anagram = anagram.substring(0,index) + anagram.substring(index +1, anagram.length());
}else{
return false;
}
}
return anagram.isEmpty();
}
/*
* Another way to check if two Strings are anagram or not in Java
* This method assumes that both word and anagram are not null and lowercase
* #return true, if both Strings are anagram.
*/
public static boolean iAnagram(String word, String anagram){
char[] charFromWord = word.toCharArray();
char[] charFromAnagram = anagram.toCharArray();
Arrays.sort(charFromWord);
Arrays.sort(charFromAnagram);
return Arrays.equals(charFromWord, charFromAnagram);
}
public static boolean checkAnagram(String first, String second){
char[] characters = first.toCharArray();
StringBuilder sbSecond = new StringBuilder(second);
for(char ch : characters){
int index = sbSecond.indexOf("" + ch);
if(index != -1){
sbSecond.deleteCharAt(index);
}else{
return false;
}
}
return sbSecond.length()==0 ? true : false;
}
}
You are adding the ascii values of characters in given strings and comparing them, which will not always give you correct results. Consider this:
String a="acd" and String b="ccb"
both of them will give you a sum of 296 but these are not anagrams.
You can count of occurrences of characters in both the string and compare them. In above example, it will give you {"a":1,"c":1,"d":1} and {"c":2,"b":1}.
Also,you can associate a prime number with each of the character set [a-z] where 'a' matches 2, 'b' matches 3, 'c' matches 5 and so on.
Next, you can calculate the multiplication of the prime numbers associated with characters in the given string. The multiplication follows associativity rules (xy = yx).
Example:
abc --> 2*3*5 = 30
cba --> 5*3*2 = 30
Note: If the string size is huge, this might not be the best approach as you might encounter overflow issues.
I am trying to write a code that will tell me if one string is a substring of another string. The catch is that it does not matter if there are characters in between and the only characters that matter are 'A', 'T', 'G' and 'C'. For instance:
"TxxAA" is a subsequence of "CTyyGCACA"
"pln" is a subsequence of "oiu"
"TAA" is NOT a subsequence of "TCCCA"
Currently I am doing
private boolean subSequence(DNASequence other) {
other.fix();
boolean valid = false;
String t = other.toString();
data = dataFix(data);
int index = 0;
for (int i = 0; i < data.length(); i++) {
for (int j = 0; j < t.length(); j++) {
if(data.charAt(i) == t.charAt(j)) {
if( j >= index) {
valid = true;
index = j;
t = t.replace(t.charAt(j), '_');
} else {
valid = false;
}
}
}
}
if (data == "" || t == "" ) {
valid = true;
}
return valid;
}
private String dataFix(String data) {
for (int i = 0; i < data.length(); i += 1) {
char ch = data.charAt(i);
if (("ATGC".indexOf(ch) < 0))
data = data.replace(data.charAt(i), ' ');
}
data = data.replaceAll(" ", "").trim();
return data;
}
the fix() and dataFix() methods erase all characters besides "ATGC". As the code iterates through, it is replacing the character in t that matches with data.charAt(i) with a _ so that it does not rematch the same letter (I was having that problem).
Currently, what is happening is that the replace function is replacing every char in the string not just the char at the specific index (which is what it is supposed to do) What is a better way to approach this problem? Where am I going wrong? Thank you.
To answer the first question 'What is a better way to approach this problem?', I would recommend using Regular Expressions (or regex). Regular Expressions are a way to express patterns in text.
For this example where you have a search term:
TxxAA
a regex to describe the patter you are looking for could be:
T.*A.*A
Without going into too much detail the term .* is an expression for any number (zero or more) of any characters. So this regex describes a pattern which is: T; then any characters; A; then any characters; and then A.
Your original question becomes "does a sequence have a sub-sequence with the pattern T.*A.*A?". Java has a regex library built in and you can use the Pattern and Matcher objects to answer this question.
Some sample code as a demonstration:
public class DnaMatcher {
static boolean isSearchChar(char c) {
return 'A' == c || 'T' == c || 'G' == c || 'C' == c;
}
static Pattern preparePattern(String searchSequence) {
StringBuilder pattern = new StringBuilder();
boolean first = false;
for (char c : searchSequence.toCharArray()) {
if (isSearchChar(c)) {
if (first) {
first = false;
} else {
pattern.append(".*");
}
pattern.append(c);
}
}
return Pattern.compile(pattern.toString());
}
static boolean contains(String sequence, String searchSequence) {
Pattern pattern = preparePattern(searchSequence);
Matcher matcher = pattern.matcher(sequence);
return matcher.find();
}
public static void main(String...none) throws Exception {
System.out.println(contains("CTyyGCACA", "TxxAA")); // true
System.out.println(contains("TCCCA", "TAA")); // false
}
}
You can see that the preparePattern matches prepares the regex expression as discussed.
Understanding that the strings might be very long, a regular expression check might take some time.
static String fix(String s) {
return s.replaceAll("[^ACGT]+", "");
}
static boolean isSubSequence(String sought, String chain) {
sought = fix(sought);
chain = fix(chain);
char[] soughtChars = sought.toCharArray();
char[] chainChars = chain.toCharArray();
int si = 0;
for (int ci = 0; si < soughtChars.length && ci < chainChars.length; ++ci) {
if (chainChars[ci] == soughtChars[si]) {
++si;
}
}
return si >= soughtChars.length;
}
Or
static boolean isSubSequence(String sought, String chain) {
sought = fix(sought);
chain = fix(chain);
int ci = 0;
for (char ch : sought.toCharArray()) {
ci = chain.indexOf(ch, ci);
if (ci < 0) {
return false;
}
++ci;
}
return true;
}
The problem seems more the sense of such a result.
Comparing with regex:
I did a comparison:
StringBuilder sb = new StringBuilder(10_000);
Random random = new Random(42);
for (int i = 0; i < 10_1000 - 6; ++i) {
sb.append("ACGT".charAt(random.nextInt(3)));
}
sb.append("TTAGTA");
String s = sb.toString();
String t = "TAGAAG";
{
long t0 = System.nanoTime();
boolean found = contains(s, t);
long t1 = System.nanoTime();
System.out.printf("Found: %s in %d ms%n", found, (t1 - t0) / 1000_000L);
}
{
long t0 = System.nanoTime();
boolean found = isSubSequence(t, s);
long t1 = System.nanoTime();
System.out.printf("Found: %s in %d ms%n", found, (t1 - t0) / 1000_000L);
}
Results
Found: false in 31829 ms --> Regex
Found: false in 5 ms --> indexOf
But: the case is quite artificial: failure on a short string.
It can be done with a (relatively) simple recursion:
/**
* Returns true is s1 is a subsequence of s2, false otherwise
*/
private static boolean isSubSeq(String s1, String s2) {
if ("".equals(s1)) {
return true;
}
String first = s1.substring(0, 1);
s1 = s1.substring(1);
int index = s2.indexOf(first);
if (index == -1) {
return false;
}
s2 = s2.substring(index+1);
return isSubSeq(s1, s2);
}
Algorithm: look for the first index of the first character of s1 in s2, if there is no such index - the answer is false, if there is, we can continue looking (recursively) for the next letter starting at position index+1
EDIT
It seems that you need to sanitize your input to include only the characters: 'A', 'T', 'G', 'C'
It's easy to do (following runs on Java 9, but it's easy to modify to lower versions of Java):
private static String sanitize(String s) {
String result = "";
List<Character> valid = List.of( 'A', 'T', 'G', 'C');
for (char c : s.toCharArray()) {
if (valid.contains(c)) {
result += c;
}
}
return result;
}
Then it's used as follows (example):
public static void main(String[] args) {
String s1 = "TxxAA";
String s2 = "CTyyGCACA";
s1 = sanitize(s1); // you need to sanitize only s1, can you see why?
System.out.println(isSubSeq(s1, s2));
}
I have a string like
Str s1 = abd,jh
Str2 = aa$$ab
I want to read string which have only a,b and $.
Str1 return false
Str2 return true.
My Code
public static boolean containsOtherCharacter(String str) {
String[] st = str.split("");
for(int x = 0; x < st.length; x++)
if (st[x].compareTo("A") != 0 && st[x].compareTo("B") != 0 && st[x].compareTo("$") != 0)
return true;
return false;
}
Any help how to read that. Any other value other then this should be ignored.
You're trying to overcomplicate things here. You could simply use a regular expression, like
String s = "aa$$ab";
System.out.println(s.replaceAll("[ab$]", "").length()==0);
It removes a, $, and b from the String. After that, if the length is greater than 0, then the String must have had some other characters.Note that it is case sensitive.
public static boolean hasSpecialChar( String input)
{
boolean found = true;
int len = input.length();
for(int i = 0; i< len ; i++)
{
if(input.charAt(i)== 97|| input.charAt(i)==98 ||input.charAt(i)==36)
{
// read the string
}
else
{
found = false;
return found;
// give out the error
}
}
return found ;
}
As you want to only read the a, b and $ thus we could use the ASCII values of the character and thus read in the string or line character by character and check the input. As the ASCII values for a is equal to 97, 98 for b and 36 for $ this would work fine for this case.
Hope is helps you!!! #shanky singh
Here's another way to solve this:
public static boolean containsOtherCharacter(String str) {
boolean The_answer=false;
int count0 = StringUtils.countMatches(str, "a");
int count1 = StringUtils.countMatches(str, "b");
int count2 = StringUtils.countMatches(str, "$");
int ans=count0+count1+count2;
if(ans==str.length())The_anser=true;
return The_answer;
}
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.