0

There is a method that I want to calculate its complexity in asymptotic notation. It calls additional methods(equals and substring) which complexity is $O(n)$. If it was $O(1)$, I could figure out complexity of the calling method. I have tried some operations; however, I'm stuck on calculation with the additional part. Can you solve its complexity with steps?

public static int func(String x)
   if(x == null || x.equals(""))
      return 0;
   else
      return 1 + func(x.substring(1));

enter image description here

3 Answers3

1

The question is absolutely not answerable without knowing what aFunctionWhichComplexityisO(n) returns.

Assuming that the function takes x as an argument, it might for example check if x contains the decimal representation of an integer y, and returns a string with the decimal representation of y-1 if y > 0, and null if y ≤ 0 or x does not contain the decimal representation of an integer.

In that case if x contains n characters, the time is $O (n·10^n)$.

Or the function always returns null, then the time is $O (n)$.

gnasher729
  • 32,238
  • 36
  • 56
1

Assuming that you have the right recurrence:

$T(n)=T(n-1)+2+(n-1)$

then you have

\begin{align*} T(n)&=T(n-1)+2+(n-1)\\ T(n-1)&=T(n-2)+2+(n-2)\\ \vdots&=\vdots\\ T(n-k)&=T(n-k)+2+(n-k)\\ \vdots&=\vdots\\ T(1)&=T(0)+2+(1)\\ \end{align*}

Summing-up you obtain:

$T(n)=T(0)+2n+\sum_{i=1}^{n-1}i=2n+\frac{n(n-1)}{2}=\frac{n(n+3)}{2}$

(recall that $T(0)=0$)

Hope this is what you expected.

Maczinga
  • 460
  • 2
  • 8
1

This all comes down to the time it takes to compute x.substring(1) and x.equals("").

If these methods were implemented in the obvious way (x.substring(1) builds up a new string by copying characters; x.equals("") compares character-by-character until the first match), then x.substring(1) would take $O(n)$ time and x.equals("") would take $O(1)$ time (because it only looks at the first character of x; if that is non-empty, it returns false).

So let's calculate the running time under the assumption that these methods are implemented in the obvious, naive way. x.substring(1) returns a string of length $n-1$. Therefore, you recursively invoke func() on a string of length $n-1$. Let $T(n)$ denote the running time of func(x) when passed a string x of length $n$. We obtain the recurrence relation

$$T(n) = T(n-1) + O(n).$$

(In other words, the recurrence you had is correct.) This recurrence solves to

$$T(n) = O(n^2);$$

see https://cs.stackexchange.com/a/2803/755 for details how. So, if those were the right assumptions about the running time of substring() and equals(), the running time of your method would be $O(n^2)$.

However, it turns out that some versions of Java implement substring() in a particularly clever way, with the result that x.substring(1) takes only $O(1)$ time, not $O(n)$ time. In particular, x.substring(1) doesn't make any copies of the string. Instead, it creates a new string object (call it y) which has a pointer to x and effectively says "hey, this string skips the first character of x and starts at index 1".

Consequently, when you take into account this more sophisticated understanding of how substring() works in some versions of Java, the recurrence becomes

$$T(n) = T(n-1) + O(1).$$

That solves to $T(n) = O(n)$. So, the total running time of your code is actually only $O(n)$, in those versions of Java, due to the clever way that Java implements the substring() method.

See https://stackoverflow.com/q/4679746/781723 to learn more about the running time of substring() and which of these analyses applies to you.

D.W.
  • 167,959
  • 22
  • 232
  • 500