21

Is there any facility in the standard Java libraries that, given a CharSequence, produces the reverse in O(1) time?

I guess this is "easy" to implement, just wondering whether it already exists. (I suspect the reason this is not offered is because the "easy" way would actually break multi-char code-points - but in many cases we know we are not dealing with those).

Thanks

Update Heh, it's a bit amusing that most thought this "impossible", good work guys! Well, actually it is (conceptually) trivial - pseudojava follows to make it clear:

class MyReverseString extends String { //of course I can't extend String!
   final String delegate;
   MyReverseString(String delegate) { this.delegate = delegate; }

   int length() { return delegate.length(); }

   int charAt(int i) { return delegate.charAt(delegate.length() - 1 - i); }
}

I'm leaving the question open for some more, just in the rare event that something like the obvious solution (e.g. see Jon Skeet's one) already exists in the JDK and someone knows about it. (Again, highly unlikely due to those nasty code points).

Edit Probably the confusion came from me having "string" in the title (but not String!), whereas I only ask for "the reverse of a CharSequence". If you were confused, sorry. I would have hoped the O(1) part would make exactly clear what was being asked for.

And by the way, this was the question that made me ask this one. (That's a case where it would be easier to run a regex from right-to-left, not left-to-right, so there may be some practical value even for the simple/broken-codepoints implementation)

Community
  • 1
  • 1
Dimitris Andreou
  • 8,806
  • 2
  • 33
  • 36
  • 6
    How would you reverse a string in O(1)? You have to iterate over the string, right? Surely the lower limit is O(n)? – Blorgbeard Jun 24 '10 at 12:33
  • 42
    In `O(1)`? Rotate your monitor 180 degrees. – Bart Kiers Jun 24 '10 at 12:33
  • O(1) does not mean that in YOUR code you accomplish the task with a single line of code w/o loops... anyway the StringBuffer() solution seems the most viable here. – Manrico Corazzi Jun 24 '10 at 12:35
  • maybe you mean O(1) space - if so you'd use a for loop to loop to from i=0..n/2 swapping char i & length-i, no extra memory being used and the original string is changed. – John Boker Jun 24 '10 at 12:36
  • interviewing for Joel? http://www.joelonsoftware.com/articles/fog0000000073.html – Will Jun 24 '10 at 12:37
  • The spec of StringBuffer.reverse indicates that it changes the semantics of surrogate pairs - http://java.sun.com/javase/6/docs/api/java/lang/StringBuffer.html#reverse() ; your suggestion would be no worse! – Pete Kirkham Jun 24 '10 at 12:50
  • You can reverse a string with n/2 steps. But that’s still O(n). – Gumbo Jun 24 '10 at 13:30
  • RE impossibility: You can create an object in O(1) time that provides methods that, when called, return the characters in reverse order. (Such as that posted by Jon Skeet.) But actually getting the list of characters in reverse order is going to take at least O(n). To say you can create the object in O(1) seems to me a rather uninteresting fact. I can surely create the object that holds the methods to do a sort in O(1), but that doesn't mean the sort will run in O(1). – Jay Jun 24 '10 at 14:11
  • @Jay, you might want to read something like http://en.wikipedia.org/wiki/Lazy_evaluation – Dimitris Andreou Jun 24 '10 at 15:48
  • @Bart, but I would get the characters upside down then!! – Dimitris Andreou Jun 24 '10 at 15:48
  • @Pete, oh my. Thanks for pointing that out, I never looked but always assumed that this would be doing the right thing. What a mess :( – Dimitris Andreou Jun 24 '10 at 15:56
  • @Gumbo, out of curiousity, can you elaborate on that? – Dimitris Andreou Jun 24 '10 at 15:58
  • @Dimitris Andreou: Sure. You have two pointers, one is pointing to the first and one to the last character in the string. Then you swap the characters the pointers are pointing at, increment the one at the start and decrement the one at the end. Repeat that step until the pointers reach each others position. – Gumbo Jun 24 '10 at 16:29
  • @Dimitris: I'm not saying that deferring evaluation is a bad idea. I'm saying that whether you do the real work at object creation time or wait until you know it's actually needed has nothing to do with how long it takes to do the work. It is surely wildly inaccurate to say, "I have an algorithm that sorts 1 billion records in .01 milliseconds", when what I really mean is that I can load the program that does the sort in that time, not that it actually runs that fast. (continued ...) – Jay Jun 24 '10 at 16:37
  • (continued) Yes, if in your application you don't need to reverse a large percentage of the strings presented to you, then writing code that avoids doing this work when it's not needed is no doubt a good thing. But that doesn't change the shape of the runtime function, it just scales it down a factor. O() measurement is about run time compared to size of the input set. If 50% of your strings are never processed, then I guess you could say your runtime is O(n)/2, but that's still O(n). n/2 != 1 unless n is guaranteed to be 2. – Jay Jun 24 '10 at 16:42
  • @Jay, in the case with the reversed string/list, _both_ objects have O(1) access to each character. – Dimitris Andreou Jun 24 '10 at 16:51
  • @Dimitris: I don't understand your point. What are the "both" objects you are referring to? In any case, I thought the question was not how long it takes to process a character, but how long it takes to process the string. If it takes O(1) for each character, then the whole process is O(n). – Jay Jun 24 '10 at 17:06
  • @Jay, the normal CharSequence and its (_constructed in O(1)_) reverse. Compare this point with your sorting example (did the lazy "sorted" object have the same complexity to access an element? Well, no.) – Dimitris Andreou Jun 24 '10 at 18:13
  • @Dimitris: I could easily create a sorter object that includes a getSortedElement(int n) function that retrieves the nth element in sorted order, and which is implemented lazily, i.e. that does not sort the elements until the first call to getSortedElement. Seems to me that Reverse.charAt(n) to return the nth character in reverse sequence and Sorter.getSortedElement(n) to return the nth element in sort order are analogous concepts. (continued -- these comment blocks are too short for serious debate/argument!) – Jay Jun 24 '10 at 21:34
  • (continued) It's true that to get any one character in reverse order here is a consistent O(1), while in my Sorter example the first element would be O(n*log(n)) or whatever I could manage, while subsequent calls would then be O(1). If the goal is to get just a single character in reverse sequence, like we just want to get the penultimate letter from each word, then a "view" solution would give O(1) performance. But if the goal is to get a String that is the reverse of the original String (or whatever), then you have to process all the characters, and you're back to O(n). – Jay Jun 24 '10 at 21:37
  • The comment area is not short, you just spend too much space repeating things. :) (Btw, the motivation for this is in my original post, at the bottom). – Dimitris Andreou Jun 24 '10 at 22:59

10 Answers10

32

Well, you can easily produce an implementation of CharSequence which returns the same length, and when asked for a particular character returns the one at length-index-1. toString() becomes O(n) of course...

Creating that reversed CharSequence would be O(1) - all it's got to do is store a reference to the original CharSequence, after all. Iterating over all the characters within the sequence is going to be O(n) though, obviously.

Note that creating a reversed CharSequence (as per the body of your question) is not the same as creating a reversed String (as per the title of your question). Actually producing the String is O(n), and has to be.

Sample code, mostly untested:

public final class ReverseCharSequence implements CharSequence
{
    private final CharSequence original;

    public ReverseCharSequence(CharSequence original)
    {
        this.original = original;
    }

    public int length()
    {
        return original.length();
    }

    public char charAt(int index)
    {
        return original.charAt(original.length() - index - 1);
    }

    public CharSequence subSequence(int start, int end)
    {
        int originalEnd = original.length() - start;
        int originalStart = original.length() - end;
        return new ReverseCharSequence(
            original.subSequence(originalStart, originalEnd));
    }

    public String toString()
    {
        return new StringBuilder(this).toString();
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    In collection terminology, I think you call this a "view", right? You can certainly create a reverse "view" of a `CharSequence` in `O(1)`, which is what you've shown here. – polygenelubricants Jun 24 '10 at 12:41
  • 2
    @polygenelubricants: Yes, that's right. And that view itself is a CharSequence, as required. If the OP wants specific operations to be O(1), he ought to clarify :) – Jon Skeet Jun 24 '10 at 12:42
  • I see what you are doing, but I doubt that this implementation will do what he wants it to do in constant time. – jjnguy Jun 24 '10 at 12:45
  • 1
    @Justin: Without further information, I think it's about as close as he'll get. The construction is certainly O(1), and anything other than `toString()` is also O(1) if it is in the underlying implementation. – Jon Skeet Jun 24 '10 at 12:49
  • No worries of course. You may see above the original question/problem which triggered me search for an O(1) string reversal (ok, ok, not "string"! "CharSequence"!). – Dimitris Andreou Jun 24 '10 at 13:41
10

This is impossible. In order to reverse a string you must process each character at least once, thus, it must be at least O(n).

jjnguy
  • 136,852
  • 53
  • 295
  • 323
  • If the downvoter actually thinks this is possible, please tell me how. – jjnguy Jun 24 '10 at 12:35
  • 3
    Nope... the downvoter thought that simpy stating "This is impossible" was not an helpful answer. After the edit, though, this person thinks exactly the opposite (so the upvote) ;-) – Manrico Corazzi Jun 24 '10 at 12:37
  • @Manrico, that makes sense. Yeah, my initial response wasn't helpful at all. I never intended to leave it that way though. – jjnguy Jun 24 '10 at 12:39
  • @Justin: The title of the question is impossible. The body isn't :) – Jon Skeet Jun 24 '10 at 12:41
  • @Jon the question is: " Is there any facility in the standard Java libraries that, given a CharSequence, produces the reverse in `O(1)` time?" Given some CharSequence, produce the reverse in constant time. – jjnguy Jun 24 '10 at 12:42
  • @Jon how could you do that in `O(1)`? – jjnguy Jun 24 '10 at 12:43
  • 5
    @Justin: *"I never intended to leave it that way though"* Before publishing the first version of your answer, ensure it's helpful, even if you plan to come back and improve it. Otherwise, what's to stop people just posting "alsdfkjalsdkjflaskdfj" and then editing it, to grab pole position? – T.J. Crowder Jun 24 '10 at 12:43
  • @T.J. My first response wasn't a garbled mess. It was a short semi-answer to the question. – jjnguy Jun 24 '10 at 12:47
  • 6
    @Justin: And yet, demonstrably people didn't think it was helpful. Not looking for an argument here, but posting unhelpful answers in order to "get there first" and then going back to edit just doesn't sit right somehow. I mean, how long does it take to type "In order to reverse a string you must process each character at least once, thus, it must be at least `O(n)`." as well? – T.J. Crowder Jun 24 '10 at 12:50
  • 2
    @Justin, nevertheless, if you knew upfront that it wasn't your final answer, why post it? And how were other people supposed to know that it wasn't your final answer? Just don't be surprised to get a (or more) down-vote(s) for such an answer. – Bart Kiers Jun 24 '10 at 12:52
  • @Bart, I was surprised because I first saw the downvote after I edited my initial answer. That is why I was surprised. I completely understand a downvote on my initial response. – jjnguy Jun 24 '10 at 12:56
  • @T.J. You are right. An initial answer shouldn't be as crappy as the one I posted. – jjnguy Jun 24 '10 at 12:56
  • @Justin: We are none of us perfect. :-) – T.J. Crowder Jun 24 '10 at 13:06
5
string result = new StringBuffer(yourString).reverse().toString();

Depending on what you understand under O(1) it does not seem possible since even reading the string once needs O(n) with n being the number of characters in the string.

Etan
  • 17,014
  • 17
  • 89
  • 148
3

StringBuffer has a reverse: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/StringBuffer.html#reverse()

BTW, I assume you meant O(n) because O(1) (as other people have mentioned) is obviously impossible.

Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
tster
  • 17,883
  • 5
  • 53
  • 72
1

How all the others wrote already, it's not possible in O(1) time, since you do have to look at least at every character once.

But if you meant how to do it in O(1) space, here's it: If you want to do it in O(1) space, you can obviously not allocate a new string of the same length, but you have to swap the characters in-place.

So all you have to do is iterate from left to the middle of the string and simultaneously from right to the middle and swap elements. Pseudocode (Conventions: Let string s be accessible at the n-th character via s[n]. If s has length k, we say it has elements 0 to k-1):

for i=0; i<len(s)/2; ++i{
 char tmp = s[i]
 s[i] = s[len(s)-1-i]
 s[len(s)-1-i] = tmp
}

So you see, all you need is a auxiliary variable tmp containing your current char for swapping it, so you can do it in O(1) space.

phimuemue
  • 34,669
  • 9
  • 84
  • 115
0
for (count = str.length()-1; count >= 0; count--) {
     tempStr = tempStr.concat(Character.toString(origStr.charAt(count)));
}
Jaikrat
  • 1,124
  • 3
  • 24
  • 45
0

Here I have a sample of the same using substring method and o(n). I am aware that using substring will hold complete string memory.

for(int i = 0; i < s.length(); i++)    {
    s = s.substring(1, s.length() - i) + s.charAt(0) + s.substring(s.length() - i);
    System.out.println(s);
}

This might help you. Tell me if I am wrong!!

Karthik GVD
  • 91
  • 1
  • 2
  • 9
0

// worked hard to figure this out

public static String ReverseString(String s){
    if (s.length()>0){
        s=s.charAt(s.length()-1)+printString(s.substring(0,s.length()-1));
    }
    return s;
}
0

Jon Skeet's solution is likely most efficient. But if you just want a quick and dirty, this should do it and I don't think it would be far behind in performance.

StringBuffer reverse=new StringBuffer(original.toString()).reverse();

A StringBuffer is a CharSequence, so if you're implying that the result must be a CharSequence, this is.

Well, this might be faster than Mr Skeet's solution if you examine the sequence multiple times, as it eliminates the overhead of the calculation to find the right char position every time you read a character. It's going to do it just once per character.

If I was going to do a billion of these, maybe I'd do a benchmark.

Jay
  • 26,876
  • 10
  • 61
  • 112
0

Better yet use StringBuilder to reverse, which is the non-synchronized version of StringBuffer.

Onur
  • 958
  • 2
  • 13
  • 18