A: 

First improvement: you can break once you find a nonmatch, right?

CPerkins
This will speed it up, but it will not change the time complexity.
alanlcode
True - but it does make it more efficient, which was part of the OP's question too, no?
CPerkins
+13  A: 

It's just O(N).

Saying O(N+3) isn't really meaningful - constant factors are ignored.

You could make it quicker by breaking out when it finds a mismatch:

bP = false;
break;

(Not that that changes the fact that it's O(N), but it will speed it up in most cases.)

This isn't true:

this code checks the string's characters to see whether it is the same as the one before it

It checks that the characters at the start match the ones at end, so in other words it's a naive palindrome checker.

Another speedup would to loop until s.length()/2 - otherwise you're doing all the comparisons twice for a string that is a palindrome.

RichieHindle
Thank you for the help, this answer and Paul's have helped me :)
Aran
+3  A: 

It is O(N). You are doing N comparisons, where N=s.length(). Each comparison takes O(1) time, as it is a single character comparison.

+3 does not matter, as asymptotic notation only cares about the highest order term.

alanlcode
A: 

You can cut the complexity of the function in half by stopping at (i == (s.length() / 2)+1) Not relevant in BigO terms, still a pretty decent gain.

Michiel Buddingh'
You don't need the `+1` - when the length is odd it's OK not to compare the middle character with itself. 8-)
RichieHindle
And quite right you are.
Michiel Buddingh'
A: 

big-o complexity is always without costants (since them, for N->oo, are unimportant). So your time complexity is simply O(n)

Also to make this more efficient, would it be good to store the character in temporary strings?

this is not your job, the JIT compiler will handle this micro optimization for you

dfa
+1  A: 

so first of all, what is the method supposed to do?

my guess: determine if a strinig is a palindorome.

quite obviously, you will not be able to get it down under O(N)

O(N+3) == O(N)

the other question is, is it the most efficient solution? maybe not.

room for improvement: 1) cut it in half. you check all characters two times.(like Michiel Buddingh suggested.)

2) obtain the char array beforehand, that spares you some index checks that occur inside chatAt()

all other operations, charAt() length() are O(1) with the standard String implementation.

Andreas Petersson
A: 

Assuming the operations in your loop can be executed in constant time, the complexity is O(N). Since "Big-O" notation measures growth, not pure speed, constant factors can be disregarded. This leaves us with the conclusion that O(N+3) is equal to O(N).

Silfverstrom
+6  A: 

The given code appears to be checking if a string is a palindrome by checking if character "N" is the same as character "length-N". As already mentioned, you can increase the efficiency by

  • only checking the first half
  • breaking out (return false) as soon as you find a non-match

To those suggestions, I'd add

  • don't recalculate s.length() repeatedly every time through the loop since it doesn't change.

Given all that:

boolean isP(String s) {
  int l = s.length();
  int l2 = l/2;
  int j = l - 1;
  for(int i=0; i<l2; i++) {
    if(s.charAt(i) != s.charAt(j)) {
        return false;
    }
    j--;
  }
  return true;
}
Paul Tomblin
+1 I just wanted to write the same.
Gumbo
Both this and Richie's answers have helped... Thanks :)
Aran
Does Java *really* recalculate `length` every time through? Is it even calculated? No, surely not … I mean, it's the year 2009, Java is 15 years old, `String`s are immutable and the optimization/inlining is *trivial*. Storing `l` and `j` instead of letting the optimizing JITter do its work seems extremely premature optimization. Summary: I think the suggestion is wrong and misleading.
Konrad Rudolph
see http://stackoverflow.com/questions/1318545/time-complexity-o-of-isp/1318637#1318637 for measurements and comparison of the 3 presented methods.
Andreas Petersson
+5  A: 

this is most likely the most efficient implementation in java:

    public static boolean isP(String s) {
    char[] chars = s.toCharArray();
    for (int i = 0; i < (chars.length / 2); i++) {
        if (chars[i] != chars[(chars.length - i - 1)]) return false;
    }
    return true;
    }

benefits:

  • returns on first sight of difference.
  • uses direct char[] access to avoid aboundary checks done in charAt
  • only iterates half the string, as opposed the full string.

is - like all other proposed solutions still O(N)

just measured the times fo the presented solutions for a really big string (times in nanoseconds)

 Aran:           32244042
 Andreas:        60787894
 Paul Tomblin:   18387532

first, the measurements above were done with the client vm. thus the calculation i < (chars.length / 2) was not inlined as a constant. using the -server Vm parameter gave a much better result:

 Aran:           18756295
 Andreas:        15048560
 Paul Tomblin:   17187100

To drive it a bit extreme:

a word of warning first:

DO NOT USE THIS CODE IN ANY PROGRAM YOU INTEND TO USE/SHIP.

it contains hidden bugs and does not obey to the java api and has not error handling, as pointed out in the comments. it serves purely to demonstrate the theoretical performance improvements obtainable by dirty tricks.

there is some overhead when copying the array from the string, because the string class internally makes a defensive copy.

if we obtain the original char[] from the string directly we can squeeze out a bit of performance, at the cost of using reflection and unsave operations on the string. this gets us another 20% performance.

public static boolean isPReflect(String s) {
    char[] chars = null;
    try {
        final Field f = s.getClass().getDeclaredField("value");
        f.setAccessible(true);
        chars = (char[]) f.get(s);
    } catch (IllegalAccessException e) {
    } catch (NoSuchFieldException e) {
    }

    final int lenToMiddle = chars.length / 2;
    for (int i = 0; i < lenToMiddle; i++) {
        if (chars[i] != chars[(chars.length - i - 1)]) return false;
    }
    return true;
}

times:

 Aran:           18756295
 Andreas1:       15048560
 Andreas2:       12094554
 Paul Tomblin:   17187100
Andreas Petersson
Pretty interesting that just switching to server-vm provides almost as much speed up as all our manual optimizations. I guess it goes to show that code optimization (as opposed to algorithm optimization) is usually a waste of time.
Paul Tomblin
Driving this to extremes, you *might* get a further speedup by starting in the middle, as that would be more likely to have better cache coherency at the beginning, which means shorter running times if differences are found close to the starting positions.
Michiel Buddingh'
just tested this, results are nearly equal but slightly worse when starting in the middle.
Andreas Petersson
While the direct access optimization is interesting, it would definitely die a quick, painful death in any serious code review. That's about as unsafe as you can program. Sorry, but 1.) value is not API, a String implementation might call it something else 2.) reflection might not be allowed in your environment (think SecurityManager) 3.) you have empty catch-blocks, which will make analyzing problems with 1. or 2. a nightmare
Joachim Sauer
Using String.value will not work, because it can be longer than the String which contains it. Your code does not take String.offset and String.count into consideration at all. See the following code: String s1 = "aba"; String s2 = s1.substring(1); System.out.println("s1 = " + s1); System.out.println("s2 = " + s2); System.out.println("isPReflect(s1) = " + isPReflect(s1)); System.out.println("isPReflect(s2) = " + isPReflect(s2));
Esko Luontola
this is true. the code will not work in these cases. it is not meant to be a practical application but a theoretical preformance improvement. for all practical means, i would use the first code given in the answer (isP()) with s.toCharArray()
Andreas Petersson
A: 

Since everybody is hung up on for-loops here :), I will present another approach. Why not use StringBuilder, and reverse the second half to compare it with the first half? There is a special case when the string has a odd length, but the middle character can be disregarded since it's always a palindrome in itself.

boolean isPalindrome(String str) {
    int halfLength = str.length() / 2;
    int rightPartStart = str.length() % 2 == 1 ? halfLength + 1 : halfLength;
    StringBuilder sb = new StringBuilder(str.substring(rightPartStart));
    return str.substring(0, halfLength).equals(sb.reverse().toString());
}
crunchdog
Don't you think sb.reverse() and sb.substring() and sb.reverse() traverse the string? All you're really doing is hiding the for-loops in somebody else's code.
Paul Tomblin
Reverse and equals most likely traverses the string, I never said they didn't. How else can you compare two strings? My point is if there is standard methods, use them (with some exceptions of course), since they are well tested.
crunchdog
@crunchdog: the question is about finding a *faster* solution, not a solution that avoids explicit loops.
Stephen C
Ok then write it in C. I was trying to make a point that traversing a string is not that hard for a CPU today, so it's better to keep to well tested standard error free code.
crunchdog
+1  A: 

First, there cannot be a single-thread solution for this problem where the "worst-case" complexity is better than O(N) for arbitrary input strings. Simply put, any algorithm must look at every character in the string in the worst case. In theory you can improve on O(N) using hardware parallelism; i.e. have an indefinitely scalable number of processors working on different portions of the string. In practice, it would be hard to achieve any speed-up at all. The cost of sending the input string (or relevant parts) to each processor is going be 'O(N)', unless there is some solution that I don't know about.

Second, as you can see O(N) behaviour isn't the final answer. You also need to consider the multiplicative constant as N -> infinity, and the lesser terms for smaller values of N.

Third, @dfa says that it is not your business to micro-optimize. He's on the right track, but I don't think it is quite as clear cut as that. IMO, it is a waste of time micro-optimizing unless 1) your application really needs to run as fast as possible, and 2) your profiling of the application shows that this particular calculation really is a significant bottle-neck.

Finally, a micro-optimization that makes a program faster for one particular hardware platform / JIT compiler, may make it slower for another one. Complex micro-optimized code is harder for a JIT compiler to generate efficient code for. And if you use reflection to access the internals of (say) the String class, your code might actually fail on some platforms. (Nothing in the Java Class Library specification says that a String has a private field called "value" that is a char[]!!!)

Stephen C
+3  A: 

Here’s another solution with two opposite pointers:

boolean isPalindrome(String s) {
    int i = 0, j = s.length() - 1;
    while (i < j && s.charAt(i) == s.charAt(j)) {
        ++i;
        --j;
    }
    return i >= j;
}

The complexity is again O(n).

Going a little more into details: Let’s say every operation costs 1 unit. Comparisons, assignments, arithmetic operations, function calls each cost 1 unit. So a call of isPalindrome costs at worst case (s is a palindrome) takes for example:

  4 + n/2 · (3 + 4) + 1
= 5 + n/2 · 7
= 5 + 7/2 · n

And since the constant factor (here 5 + 7/2) is omitted, we end up with 5 + 7/2 · nO(n).

Gumbo