Short answer: str slices, in general, copy. That means that your function that does a slice for each of your string's n suffixes is doing O(n2) work. That said, you can avoid copies if you can work with bytes-like objects using memoryviews to get zero-copy views of the original bytes data. See How you can do zero copy slicing below for how to make it work.

Long answer: (C)Python str do not slice by referencing a view of a subset of the data. There are exactly three modes of operation for str slicing:

  1. Complete slice, e.g. mystr[:]: Returns a reference to the exact same str (not just shared data, the same actual object, mystr is mystr[:] since str is immutable so there is no risk to doing so)
  2. The zero length slice and (implementation dependent) cached length 1 slices; the empty string is a singleton (mystr[1:1] is mystr[2:2] is ''), and low ordinal strings of length one are cached singletons as well (on CPython 3.5.0, it looks like all characters representable in latin-1, that is Unicode ordinals in range(256), are cached)
  3. All other slices: The sliced str is copied at creation time, and thereafter unrelated to the original str

The reason why #3 is the general rule is to avoid issues with large str being kept in memory by a view of a small portion of it. If you had a 1GB file, read it in and sliced it like so (yes, it's wasteful when you can seek, this is for illustration):

with open(myfile) as f:
    data = f.read()[-1024:]

then you'd have 1 GB of data being held in memory to support a view that shows the final 1 KB, a serious waste. Since slices are usually smallish, it's almost always faster to copy on slice instead of create views. It also means str can be simpler; it needs to know its size, but it need not track an offset into the data as well.

How you can do zero copy slicing

There are ways to perform view based slicing in Python, and in Python 2, it will work on str (because str is bytes-like in Python 2, supporting the buffer protocol). With Py2 str and Py3 bytes (as well as many other data types like bytearray, array.array, numpy arrays, mmap.mmaps, etc.), you can create a memoryview that is a zero copy view of the original object, and can be sliced without copying data. So if you can use (or encode) to Py2 str/Py3 bytes, and your function can work with arbitrary bytes-like objects, then you could do:

def do_something_on_all_suffixes(big_string):
    # In Py3, may need to encode as latin-1 or the like
    remaining_suffix = memoryview(big_string)
    # Rather than explicit loop, just replace view with one shorter view
    # on each loop
    while remaining_suffix:  # Stop when we've sliced to empty view
        some_constant_time_operation(remaining_suffix)
        remaining_suffix = remaining_suffix[1:]

The slices of memoryviews do make new view objects (they're just ultra-lightweight with fixed size unrelated to the amount of data they view), just not any data, so some_constant_time_operation can store a copy if needed and it won't be changed when we slice it down later. Should you need a proper copy as Py2 str/Py3 bytes, you can call .tobytes() to get the raw bytes obj, or (in Py3 only it appears), decode it directly to a str that copies from the buffer, e.g. str(remaining_suffix[10:20], 'latin-1').

Answer from ShadowRanger on Stack Overflow
Top answer
1 of 3
58

Short answer: str slices, in general, copy. That means that your function that does a slice for each of your string's n suffixes is doing O(n2) work. That said, you can avoid copies if you can work with bytes-like objects using memoryviews to get zero-copy views of the original bytes data. See How you can do zero copy slicing below for how to make it work.

Long answer: (C)Python str do not slice by referencing a view of a subset of the data. There are exactly three modes of operation for str slicing:

  1. Complete slice, e.g. mystr[:]: Returns a reference to the exact same str (not just shared data, the same actual object, mystr is mystr[:] since str is immutable so there is no risk to doing so)
  2. The zero length slice and (implementation dependent) cached length 1 slices; the empty string is a singleton (mystr[1:1] is mystr[2:2] is ''), and low ordinal strings of length one are cached singletons as well (on CPython 3.5.0, it looks like all characters representable in latin-1, that is Unicode ordinals in range(256), are cached)
  3. All other slices: The sliced str is copied at creation time, and thereafter unrelated to the original str

The reason why #3 is the general rule is to avoid issues with large str being kept in memory by a view of a small portion of it. If you had a 1GB file, read it in and sliced it like so (yes, it's wasteful when you can seek, this is for illustration):

with open(myfile) as f:
    data = f.read()[-1024:]

then you'd have 1 GB of data being held in memory to support a view that shows the final 1 KB, a serious waste. Since slices are usually smallish, it's almost always faster to copy on slice instead of create views. It also means str can be simpler; it needs to know its size, but it need not track an offset into the data as well.

How you can do zero copy slicing

There are ways to perform view based slicing in Python, and in Python 2, it will work on str (because str is bytes-like in Python 2, supporting the buffer protocol). With Py2 str and Py3 bytes (as well as many other data types like bytearray, array.array, numpy arrays, mmap.mmaps, etc.), you can create a memoryview that is a zero copy view of the original object, and can be sliced without copying data. So if you can use (or encode) to Py2 str/Py3 bytes, and your function can work with arbitrary bytes-like objects, then you could do:

def do_something_on_all_suffixes(big_string):
    # In Py3, may need to encode as latin-1 or the like
    remaining_suffix = memoryview(big_string)
    # Rather than explicit loop, just replace view with one shorter view
    # on each loop
    while remaining_suffix:  # Stop when we've sliced to empty view
        some_constant_time_operation(remaining_suffix)
        remaining_suffix = remaining_suffix[1:]

The slices of memoryviews do make new view objects (they're just ultra-lightweight with fixed size unrelated to the amount of data they view), just not any data, so some_constant_time_operation can store a copy if needed and it won't be changed when we slice it down later. Should you need a proper copy as Py2 str/Py3 bytes, you can call .tobytes() to get the raw bytes obj, or (in Py3 only it appears), decode it directly to a str that copies from the buffer, e.g. str(remaining_suffix[10:20], 'latin-1').

2 of 3
4

It all depends on how big your slices are. I threw together the following two benchmarks. The first slices the entire string and the second only a little bit. Curve fitting with this tool gives

# s[1:-1]
y = 0.09 x^2 + 10.66 x - 3.25

# s[1:1000]
y = -0.15 x + 17.13706461

The first looks quite linear for slices of strings up to 4MB. I guess this really measures the time taken to construct a second string. The second is pretty constant, although it's so fast it's probably not that stable.

import time

def go(n):
    start = time.time()
    s = "abcd" * n
    for j in xrange(50000):

        #benchmark one
        a = s[1:-1]

        #benchmark two
        a = s[1:1000]

    end = time.time()
    return (end - start) * 1000

for n in range(1000, 100000, 5000):
    print n/1000.0, go(n)
๐ŸŒ
Reddit
reddit.com โ€บ r/learnpython โ€บ algorithm complexity with strings and slices
r/learnpython on Reddit: Algorithm complexity with strings and slices
December 1, 2019 -

Recently I was thinking about interview questions I got as an undergrad:
Things like "reverse a string" and "check if a string is a palindrome".

I did most of these in C++ with a loop and scrolling through the index using logic.

When I learned Python, I realized that I could "reverse a string" by simply going:

return mystring[::-1]

Likewise with "check if it is a palindrome" by doing:

return mystring == mystring[::-1]

The problem now is that, I don't know what kinda complexity it is.

From my point of view, it is constant, so O(1). But I am guessing that that is too good to be true as the string splicing is doing something behind the scenes.

Can anyone help me clarify?

Top answer
1 of 2
2

The python page on time-complexity shows that slicing lists has a time-complexity of O(k), where "k" is the length of the slice. That's for lists, not strings, but the complexity can't be O(1) for strings since the slicing must handle more characters as the size is increased. At a guess, the complexity of slicing strings would also be O(k). We can write a little bit of code to test that guess:

import time

StartSize = 2097152

size = StartSize
for _ in range(10):
    # create string of size "size"
    s = '*' * size

    # now time reverse slice
    start = time.time()
    r = s[::-1]
    delta = time.time() - start

    print(f'Size {size:9d}, time={delta:.3f}')

    # double size of the string
    size *= 2

This uses a simple method of timing. Other tools exist, but this is simple. When run I get:

$ python3 test.py
Size   2097152, time=0.006
Size   4194304, time=0.013
Size   8388608, time=0.024
Size  16777216, time=0.050
Size  33554432, time=0.098
Size  67108864, time=0.190
Size 134217728, time=0.401
Size 268435456, time=0.808
Size 536870912, time=1.610
Size 1073741824, time=3.192

which shows the time doubles when doubling the size of the string for each reverse slice. So O(n) (k == n for whole-string slicing).

Edit: spelling.

2 of 2
1

How difficult an algorithm is to write and how difficult it is to calculate are two separate things. Creating a reversed string with the shorthand still requires n order space and n order time. Keep in mind that, in most cases, creating a reversed array isn't necessary, you can just start at the top and go down, which is essentially what Python's reversed() function does

Discussions

Time complexity of python slice operation - ๐Ÿ’ก-string-window - Coding Blocks Discussion Forum
what is the time complexity of slicing in python. Example: lis[2:] I wrote a program in leetcode with lis slicing and without lis slicing and still got the same time result. More on discuss.codingblocks.com
๐ŸŒ discuss.codingblocks.com
4
0
August 1, 2020
python - What is the time complexity of string slice? O(k) or O(n) - Stack Overflow
I understand its extracting only ... the slice? My thought process is that the conversion of the entire string into a list alone would cost O(n). Unless only part of the string gets converted into a list? So can someone please explain is string slicing on Python O(k) or ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
Time Complexity on string slicing operation in python 3 - Stack Overflow
Given a string s of length n, the slicing operation s[i : j] in Python 3, where (0 More on stackoverflow.com
๐ŸŒ stackoverflow.com
July 20, 2017
What is the time complexity of slicing a list?
O(n) where n is the length of the slice. Slicing is just "copy part of the list" so time complexity is the same as copy. More on reddit.com
๐ŸŒ r/learnpython
13
7
January 4, 2021
People also ask

What is string slicing in Python?
String slicing in Python refers to the process of extracting a portion (substring) of a string by specifying the start and end indices. It allows you to work with substrings within a larger string, making it easier to manipulate and analyze text data.
๐ŸŒ
upgrad.com
upgrad.com โ€บ home โ€บ tutorials โ€บ software & tech โ€บ string slicing in python
String Slicing in Python
What is the difference between string slicing and string indexing?
String slicing extracts a range of characters from a string, returning a new string. String indexing, on the other hand, extracts a single character from a string at a specific position, returning a character (string of length 1).
๐ŸŒ
upgrad.com
upgrad.com โ€บ home โ€บ tutorials โ€บ software & tech โ€บ string slicing in python
String Slicing in Python
Are there any performance considerations when using string slicing?
String slicing in Python is efficient and typically has a time complexity of O(k), where k is the length of the slice. However, creating many slices of a large string can increase memory usage. If performance is a concern, consider using other data structures or techniques for text processing.
๐ŸŒ
upgrad.com
upgrad.com โ€บ home โ€บ tutorials โ€บ software & tech โ€บ string slicing in python
String Slicing in Python
๐ŸŒ
Coding Blocks
discuss.codingblocks.com โ€บ t โ€บ time-complexity-of-python-slice-operation โ€บ 99497
Time complexity of python slice operation - ๐Ÿ’ก-string-window - Coding Blocks Discussion Forum
August 1, 2020 - what is the time complexity of slicing in python. Example: lis[2:] I wrote a program in leetcode with lis slicing and without lis slicing and still got the same time result.
๐ŸŒ
Leyaa
leyaa.ai โ€บ codefly โ€บ learn โ€บ python โ€บ part-2 โ€บ python-string-slicing-behavior โ€บ complexity
String slicing behavior in Python Time Complexity - Big O Analysis | Leyaa.ai
[OK] Correct: Actually, slicing copies each character in the slice, so bigger slices take more time. ... Understanding how slicing works helps you explain performance when working with strings in real projects.
๐ŸŒ
Upgrad
upgrad.com โ€บ home โ€บ tutorials โ€บ software & tech โ€บ string slicing in python
String Slicing in Python
October 22, 2024 - String indexing, on the other hand, extracts a single character from a string at a specific position, returning a character (string of length 1). String slicing in Python is efficient and typically has a time complexity of O(k), where k is the ...
Find elsewhere
๐ŸŒ
Medium
andrewwhit.medium.com โ€บ time-complexity-of-string-slicing-in-python-db25177d0c48
Time Complexity of String Slicing in Python | by Andrew | Feb, 2026 | Medium
February 22, 2026 - String slicing in Python is a powerful and concise operation, but its time complexity is O(k), where k is the length of the resulting substring. This is because strings are immutable, and slicing creates a new string by copying k characters ...
๐ŸŒ
Python
python-list.python.narkive.com โ€บ oOFdL6yB โ€บ time-complexity-of-string-operations
Time Complexity of String Operations
Actually, it is roughly linear, at least for reasonable string lengths: $ python -V Python 2.5.2 $ python -mtimeit -s "n=1000; a='#'*n" "a+a" 1000000 loops, best of 3: 1 usec per loop $ python -mtimeit -s "n=10000; a='#'*n" "a+a" 100000 loops, best of 3: 5.88 usec per loop $ python -mtimeit ...
๐ŸŒ
CSDN
devpress.csdn.net โ€บ python โ€บ 6304528ec67703293080b167.html
Time complexity of string slice - Python - DevPress - CSDN
August 23, 2022 - Answer a question What's the time complexity of slicing a Python string? Given that Python strings are immutable, I can imagine slicing them being either O(1) or O(n) depending upon how slicing is imp Mangs Python
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ python โ€บ python-frequency-of-k-in-sliced-string
Python - Frequency of K in sliced String - GeeksforGeeks
April 5, 2023 - Time complexity: O(n), where n is the length of the sliced string, as the code needs to traverse the entire sliced string to count the frequency of K. Auxiliary space: O(1), as the code, only uses a constant amount of memory regardless of the ...
๐ŸŒ
AlgoCademy
algocademy.com โ€บ link
Time Complexity Practice 2 in Python | AlgoCademy
We can optimize our approach by understanding the time complexity of each operation: Slicing: Slicing a string s[start:end] is an O(k) operation, where k is the length of the slice...
๐ŸŒ
Ancisoft
ancisoft.com โ€บ blog โ€บ time-complexity-of-string-slice
What's the Time Complexity of Python String Slicing? Implications for Iterating Over Suffixes of Large Strings โ€” ancisoft.com
Iterating over all suffixes of a string (e.g., for i in range(len(s)): process(s[i:])) is a common task in applications like text processing, pattern matching, or suffix array construction. But with large strings, this approach becomes surprisingly inefficient. ... The total time complexity of iterating over all suffixes with slicing is the sum of the costs of each individual slice:
๐ŸŒ
Stack Overflow
stackoverflow.com โ€บ questions โ€บ 62138656 โ€บ python-slice-complexity
algorithm - Python slice complexity? - Stack Overflow
If abs(m - n) is bounded by a constant, the loop only executes a constant number of times. ... Python's time complexity is O(k) where k is the length of the slice, so your function is O(n * k), which is not linear, yet still not exponential.
๐ŸŒ
LeetCode
leetcode.com โ€บ problems โ€บ reverse-words-in-a-string-iii โ€บ solutions โ€บ 199364 โ€บ Python-Solution-What-is-the-time-complexity
Python Solution - What is the time complexity? - LeetCode
November 29, 2018 - Can you solve this real interview question? Reverse Words in a String III - Given a string s, reverse the order of characters in each word within a sentence while still preserving whitespace and initial word order. Example 1: Input: s = "Let's take LeetCode contest" Output: "s'teL ekat edoCteeL tsetnoc" Example 2: Input: s = "Mr Ding" Output: "rM gniD" Constraints: * 1
Top answer
1 of 3
1

Your algorithm (in terms of number of comparisons) is O(n), where n is length of the string. In the worst case both string and pattern will be the same and then for every character in subStr you will move to next character of string. It'll be equivalent to simple comparison of strings.

However your implementation may be O(n^2) in terms of other operations and the reason for this, as you mentioned in you question, is the following line:

fn = fn[index+1 ::]

This is effectively copying the string (assuming the slice above is implemented as a copy). If you consider previous example again, for every character in a string you'd have to copy all remaining characters, which is O(n^2). This is because you'll be copying n-1 characters first, then n-2, n-3 and so on, and at the last iteration you will copy just one character. Total amount of items to be copied will be then n-1+n-2+...+1, which, as the arithmetic progression, is equal to (n-1)*((n-1)+1)/2 = (n-1)*n/2 = O(n^2). For other situations this could be generalised to O(m*n), where m is length of the pattern.

What your friend might like to tell you was: your algorithm is linear, but your implementation is not. It can be easily solved though. Use solution presented by @thkang or something more transparent to get rid of hidden complexity, for example:

try:
  si = iter(string)
  for c in subStr:
    while c != si.next():
      pass
except StopIteration:
  print "no match"
else:
  print "match"
2 of 3
1

Sorry to say but this is neither O(n) nor O(n+m). Both of them are better than O(n^2) and this algorithm is O(n^2). Why?

  1. You iterate over source string which has O(n) complexity
  2. In each iteration you call "index" on string which also has O(n) complexity

So this has worst case performance bounded by O(n^2) and is in fact an implementation of naive search algorithm. If you want to see O(n)/O(mn) I advise checking out Boyer-Moore algorithm

๐ŸŒ
Quora
quora.com โ€บ What-is-the-time-complexity-of-removing-the-first-character-in-a-Python-string
What is the time complexity of removing the first character in a Python string? - Quora
For example let to search string โ€™aโ€™*m+โ€™bโ€™ in string โ€˜aโ€™*n (m < n). First Python searches the first character โ€˜aโ€™ and find it at position 0. Then it tests whether the characters of the string in which it searches match the rest of string which it searches. It takes O(m) time because all characters except the last one match. It fails on the last character (โ€˜bโ€™), so it tries to search the fi ... In CPython (the main implementation of Python) the time complexity of the find() function is O((n-m)*m) where n is the size of the string in which you search, and m is the size of the string which you search.