The fastest method of determining if a string is a palindrome - java

I need an algorithm that verify with the fastest possible execution time, if a string is a palindrome ( the string can be a proposition with uppercase or lowercase letter, spaces etc.). All of this in Java. I got a sample :
bool isPalindrome(string s) {
int n = s.length();
s = s.toLowerCase();
for (int i = 0; i < (n / 2) + 1; ++i) {
if (s.charAt(i) != s.charAt(n - i - 1)) {
return false;
}
}
return true;
}
I transformed the string in lowercase letter using .toLowerCase() function, but I don't know how much it affects the execution time .
And as well I don't know how to solve the problem with punctuation and spaces between words in a effective way.

I think you can just check for string reverse, not?
StringBuilder sb = new StringBuilder(str);
return str.equals(sb.reverse().toString());
Or, for versions earlier than JDK 1.5:
StringBuffer sb = new StringBuffer(str);
return str.equals(sb.reverse().toString());

This avoids any copying. The functions isBlank and toLowerCase are rather unspecified in your question, so define them the way you want. Just an example:
boolean isBlank(char c) {
return c == ' ' || c == ',';
}
char toLowerCase(char c) {
return Character.toLowerCase(c);
}
Don't worry about the costs of method calls, that's what the JVM excels at.
for (int i = 0, j = s.length() - 1; i < j; ++i, --j) {
while (isBlank(s.charAt(i))) {
i++;
if (i >= j) return true;
}
while (isBlank(s.charAt(j))) {
j--;
if (i >= j) return true;
}
if (toLowerCase(s.charAt(i)) != toLowerCase(s.charAt(j))) return false;
}
return true;
Try to benchmark this... I'm hoping mu solution could be the fastest, but without measuring you never know.

Your solution seems just fine when it comes to effectiveness.
As for your second problem, you can just remove all spaces and dots etc before you start testing:
String stripped = s.toLowerCase().replaceAll("[\\s.,]", "");
int n = stripped.length();
for (int i = 0; i < (n / 2) + 1; ++i) {
if (stripped.charAt(i) != stripped.charAt(n - i - 1)) {
...

Effective is not the same of efficient.
Your answer is effective as long you consider spaces, special characters and so on. Even accents could be problematic.
About efficiency, toLowerCase is O(n) and any regexp parsing will be O(n) also. If you are concerning about that, convert and compare char by char should be the best option.

Here is my try:
public static boolean isPalindrome(String s)
{
int index1 = 0;
int index2 = s.length() -1;
while (index1 < index2)
{
if(s.charAt(index1) != s.charAt(index2))
{
return false;
}
index1 ++;
index2 --;
}
return true;
}

Here's some insight to my way of detecting a palindrome using Java. Feel free to ask question :) Hope I could help in some way....
import java.util.Scanner;
public class Palindrome {
public static void main(String[]args){
if(isReverse()){System.out.println("This is a palindrome.");}
else{System.out.print("This is not a palindrome");}
}
public static boolean isReverse(){
Scanner keyboard = new Scanner(System.in);
System.out.print("Please type something: ");
String line = ((keyboard.nextLine()).toLowerCase()).replaceAll("\\W","");
return (line.equals(new StringBuffer(line).reverse().toString()));
}
}

In normal cases :
StringBuilder sb = new StringBuilder(myString);
String newString=sb.reverse().toString();
return myString.equalsIgnoreCase(newString);
In case of case sensitive use :
StringBuilder sb = new StringBuilder(myString);
String newString=sb.reverse().toString();
return myString.equals(newString);

Related

Why is StringBuilder.reverse faster than appending to linkedlists

Leetcode problem 125. Valid Palindromeļ¼š
Given a string s, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.
Example 1:
Input: s = "A man, a plan, a canal: Panama"
Output: true
Explanation: "amanaplanacanalpanama" is a palindrome.
I appended each character into two linked lists, one forwards and one backwards, and compared them. However, I did not pass the time limit. The Leetcode's solution used a StringBuilder and reversed it. I heard that StringBuilder is implemented similar to a linked list. I have no idea why my code is much slower than the solution. I would appreciate any feedback or insights on this topic. Thank you in advance.
My code:
class Solution {
public boolean isPalindrome(String s) {
LinkedList<Character> forward = new LinkedList<Character>();
LinkedList<Character> backward = new LinkedList<Character>();
for(int i = 0 ; i < s.length() ; i++){
char ch = s.charAt(i);
if(Character.isLetterOrDigit(ch)){
if(Character.isLetter(ch)) ch = Character.toLowerCase(ch);
forward.addLast(ch);
backward.addFirst(ch);
}
}
for(int i = 0 ; i < forward.size() ; i++){
if(forward.get(i) != backward.get(i)) return false;
}
return true;
}
}
Leetcode Solution:
class Solution {
public boolean isPalindrome(String s) {
StringBuilder builder = new StringBuilder();
for (char ch : s.toCharArray()) {
if (Character.isLetterOrDigit(ch)) {
builder.append(Character.toLowerCase(ch));
}
}
String filteredString = builder.toString();
String reversedString = builder.reverse().toString();
return filteredString.equals(reversedString);
}
}
If you look at how reverse() method is implemented in AbstractStringBuilder you can see that it uses array to store characters. It is the main difference between StringBuilder and your solution. forward.get(i) and backward.get(i) have O(n) complexity, when value[j] has O(1).
Java 8 implementation:
public AbstractStringBuilder reverse() {
boolean hasSurrogates = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; j--) {
int k = n - j;
char cj = value[j];
char ck = value[k];
value[j] = ck;
value[k] = cj;
if (Character.isSurrogate(cj) ||
Character.isSurrogate(ck)) {
hasSurrogates = true;
}
}
if (hasSurrogates) {
reverseAllValidSurrogatePairs();
}
return this;
}
Actually, Leetcode solution does not seem to be the best and StringBuilder::reverse method does not have to be used at all to detect a palindrome, because it is possible to check the characters from the start and end of the string to its center and as soon as unmatching pair is found the string is NOT a palindrome.
Also conversion of StringBuilder to direct and reversed strings is redundant.
class Solution {
public boolean isPalindrome(String s) {
StringBuilder builder = new StringBuilder(s.length());
for (char ch : s.toCharArray()) {
if (Character.isLetterOrDigit(ch)) {
builder.append(Character.toLowerCase(ch));
}
}
for (int i = 0, n = builder.length(), m = n / 2; i < m; i++) {
if (builder.charAt(i) != builder.charAt(n - i - 1)) {
return false;
}
}
return true;
}
}
Similar solution using Stream API and a regexp to clean out non-letter and non-digit characters (in Unicode):
public static boolean isPalindrome(String s) {
String str = s.replaceAll("[^\\p{L}\\p{N}]", "").toLowerCase();
final int n = str.length();
return IntStream.range(0, n / 2)
.allMatch(i -> str.charAt(i) == str.charAt(n - 1 - i));
}

What's the best way to separate numbers manually for learning algorithm

I am learning Java and wonder how I can get two numbers in same line.
Is this algorithm is okay, what can I do improve? What can you suggest me?
import java.util.Scanner;
public class Main{
public static int Separate(String Values, int Order){
String toReturn = "";
int Counter = 0;
for(int Iterator = 0; Iterator < Values.length(); Iterator = Iterator + 1){
if(Values.charAt(Iterator) == ' ') {
if(Order == Counter) break;
else{
toReturn = "";
Counter = Counter + 1;
}
}
else toReturn += Values.charAt(Iterator);
}
return Integer.parseInt(toReturn);
}
public static void main(String[] args){
Scanner Entry = new Scanner(System.in);
System.out.print("Enter two numbers separated by space: ");
String Number = Entry.nextLine();
int Frst = Separate(Number, 0);
int Scnd = Separate(Number, 1);
}
}
what can I do improve? What can you suggest me?
Adopt the Java Naming Conventions:
Method Names are camelCase, starting with a lower case letter
Field and Property Names and Method Argument Names are camelCase, too
Basically only Class and Interface Names start with an upper case letter in Java.
public static int separate(String values, int order){
String toReturn = "";
int counter = 0;
for(int iterator = 0; ...) { ...
Else I'd say: This algorithm is pretty solid for a beginner. It's easy to understand what's going on.
Of course Java provides much more sophisticated tools to solve this, using for example Regular Expressions with myString.split(...), or Streams with IntStream intStream = myString.chars().
Last but not least you could add Exception Handling: What happens if Integer.parseInt is given some non-number? It will crash.
try {
return Integer.parseInt(toReturn);
} catch (NumberFormatException e) {
// when "toReturn" cannot be parsed to an int, return a
// default value instead of crashing your application
return 0;
}
Or if crashing is the desired behavior, or you can ensure that this method is never called with an illegal String, leave it as it is (= don't add try catch)
I think what you've done is great for well-formatted input, where you have a single space character between the numbers. As others have pointer out, following Java naming conventions will greatly improve the readability of your code.
Handling sequences of space characters, possible before, between, and after your numbers is a little tricky. The general pattern would be to consume any sequences of spaces, remember the current position, consume the sequence of digits, then if we're at the correct position return the parsed number.
public static int separate(String str, int order)
{
for(int i = 0, pos = 0; ; pos++)
{
while(i < str.length() && str.charAt(i) == ' ') i += 1;
int j = i;
while(i < str.length() && str.charAt(i) != ' ') i += 1;
if(i == j) throw new IllegalStateException("Missing number!");
if(order == pos)
{
// handle NumberFormatException
return Integer.parseInt(str.substring(j, i));
}
}
}
Test:
String s = " 23432 798 44";
for(int i=0; i<3; i++)
System.out.print(separate(s, i) + " ");
Output:
23432 798 44

Why my code for prefixAgain is not working?

Given a string, consider the prefix string made of the first N chars of the string. Does that prefix string appear somewhere else in the string? Assume that the string is not empty and that N is in the range 1..str.length().
public boolean prefixAgain(String str, int n) {
String res = "";
String res1 = "";
String s = str.substring(0,n);
for ( int i = 0 ; i < n ; i++ ) {
res += str.charAt(i) ;
if (s.equalsIgnoreCase(res)); {
return true;
} else {
return false;
}
}
}
There are many problems with your solution:
Why do you need to loop only till n in the prefixAgain method? You probably need to go till str.length()
Your res variable will again be a prefix of the string and will be of no use.
Why are you having ; after the if?
Using += on Strings in a loop can be very expensive. You should always consider using StringBuilder and it's append method.
The following method does what you want:
public boolean prefixAgain(String str, int n) {
if (str.length() == 1) return false;
String s = str.substring(0, n);
return str.substring(1).contains(s);
}
The main idea is to just search the required prefix in the substring starting from 2nd character (the character at index 1).
Keep it simple. :)

Taking a long string of numbers and removing any zeroes in front of it, then returning that altered string (in Java)

So I have a public static method 'getWithoutLeadingZeroes', which gets passed a String and simply needs to return it without any zeroes prefixing the string of numbers.
Now, I know that I need to iterate through the string until I find the first non-zero char in the string, but I'm not exactly sure how to take the point where the method finds the non-zero char and start copying the remainder of the String into a new String, then returning it.
Here's what I have so far:
public static String getWithoutLeadingZeroes(String s) {
boolean notZero = false;
char[] t = new char[x];
for(int i = 0; i<s.length(); i++){
if(s.charAt(i) == 0){
notZero = false;
} else {
notZero = true;
}
if(notZero = true){
for(int j = index.charAt(i))
}
return ""; //to be completed
}
I created a boolean variable to stop the loop once it hits the non-zero char and I'm pretty positive the first half of the code is accurate, but its the creating of the new String to be returned that I'm a bit stuck on. Any suggestions would be welcome.
You are NOT stopping your loop.
You could do that using the break keyword.
And: your comparison is wrong, it should read
if (... == '0'
You could use String#substring(int beginIndex) instead:
public static String getWithoutLeadingZeroes(String s) {
int i = 0;
while (i < s.length() && s.charAt(i) == '0') i++;
return s.substring(i);
}
How about the following way?
public static String getWithoutLeadingZeroes(String s) {
while(s.startsWith('0'))
s = s.substring(1);
return s;
}
This should do it
String nmbrStr = "1001234";
String cleanedStr = nmbrStr; // this way the whole number will be
// returned if in has no leading zeroes
for (int i = 0; i < nmbrStr.length(); i++) {
if (nmbrStr.charAt(i) != '0') {
cleanedStr = nmbrStr.substring(i);
break;
}
}
System.out.println(cleanedStr);
You can also use Integer.parseInt(nmbrStr) which is a lot cleaner
A simple solution.
public static String getWithoutLeadingZeroes(String stringOfNumbers) {
return String.valueOf(Long.parseLong(stringOfNumbers));
}
Another alternative solution:
public String getWithoutLeadingZeroes(String str) {
int from = 0;
for (int i = 0; i < str.length(); i++) {
if(str.charAt(i) == '0'){
from = i;
}else{
break;
}
}
return str.substring(from+1);
}
You can then make it a little bit more robust by implementing try/catch blocks and possibly along with cases that deal with unexpected input e.g null parameter or a zero-length string etc.

Substring alternative

So I'm creating a program that will output the first character of a string and then the first character of another string. Then the second character of the first string and the second character of the second string, and so on.
I created what is below, I was just wondering if there is an alternative to this using a loop or something rather than substring
public class Whatever
{
public static void main(String[] args)
{
System.out.println (interleave ("abcdefg", "1234"));
}
public static String interleave(String you, String me)
{
if (you.length() == 0) return me;
else if (me.length() == 0) return you;
return you.substring(0,1) + interleave(me, you.substring(1));
}
}
OUTPUT: a1b2c3d4efg
Well, if you really don't want to use substrings, you can use String's toCharArray() method, then you can use a StringBuilder to append the chars. With this you can loop through each of the array's indices.
Doing so, this would be the outcome:
public static String interleave(String you, String me) {
char[] a = you.toCharArray();
char[] b = me.toCharArray();
StringBuilder out = new StringBuilder();
int maxLength = Math.max(a.length, b.length);
for( int i = 0; i < maxLength; i++ ) {
if( i < a.length ) out.append(a[i]);
if( i < b.length ) out.append(b[i]);
}
return out.toString();
}
Your code is efficient enough as it is, though. This can be an alternative, if you really want to avoid substrings.
This is a loop implementation (not handling null value, just to show the logic):
public static String interleave(String you, String me) {
StringBuilder result = new StringBuilder();
for (int i = 0 ; i < Math.max(you.length(), me.length()) ; i++) {
if (i < you.length()) {
result.append(you.charAt(i)); }
if (i < me.length()) {
result.append(me.charAt(i));
}
}
return result.toString();
}
The solution I am proposing is based on the expected output - In your particular case consider using split method of String since you are interleaving by on character.
So do something like this,
String[] xs = "abcdefg".split("");
String[] ys = "1234".split("");
Now loop over the larger array and ensure interleave ensuring that you perform length checks on the smaller one before accessing.
To implement this as a loop you would have to maintain the position in and keep adding until one finishes then tack the rest on. Any larger sized strings should use a StringBuilder. Something like this (untested):
int i = 0;
String result = "";
while(i <= you.length() && i <= me.length())
{
result += you.charAt(i) + me.charAt(i);
i++;
}
if(i == you.length())
result += me.substring(i);
else
result += you.substring(i);
Improved (in some sense) #BenjaminBoutier answer.
StringBuilder is the most efficient way to concatenate Strings.
public static String interleave(String you, String me) {
StringBuilder result = new StringBuilder();
int min = Math.min(you.length(), me.length());
String longest = you.length() > me.length() ? you : me;
int i = 0;
while (i < min) { // mix characters
result.append(you.charAt(i));
result.append(me.charAt(i));
i++;
}
while (i < longest.length()) { // add the leading characters of longest
result.append(longest.charAt(i));
i++;
}
return result.toString();
}

Categories

Resources