MarcAS's blog

By MarcAS, 16 months ago, In English

Hello, this a brief post about some interesting suffix array properties that are needed in some problems such as Oolimry and Suffix Array and the recently added Inverse Suffix Array problem on CSES.

Suffix array properties

Reminders:

  • Given two strings $$$s$$$ and $$$t$$$, $$$s$$$ is lexicographically smaller than $$$t$$$ if and only if $$$s$$$ is a proper prefix of $$$t$$$ or there is some $$$i \lt \min(|s|,|t|)$$$ where $$$s[i] \lt t[i]$$$ and $$$s[j]=t[j]$$$ $$$\forall j$$$ such that $$$0\leq j \lt i$$$. In this case we will say $$$s \lt t$$$.
  • Given a string $$$s$$$, the suffix array for $$$s$$$ is a permutation $$$p$$$ where $$$p[i]$$$ is the start of the $$$i$$$th smallest suffix of $$$s$$$ in lexicographical order.
  • If $$$inv$$$ is the inverse permutation of the suffix array $$$p$$$ of $$$s$$$, then $$$inv[i] \lt inv[j]$$$ implies that $$$s[i...|s|-1] \lt s[j...|s|-1]$$$.

Basic property

If $$$s \lt t$$$ then $$$s[0]\leq t[0]$$$, otherwise we'd get that $$$s[0] \gt t[0]\implies s \gt t$$$.

By the definition of $$$p$$$, we know that $$$\forall i,j$$$ where $$$0\leq j \lt i \lt |s|:s[p[j]...n-1] \lt s[p[i]...n-1]\implies s[p[j]]\leq s[p[i]]$$$ meaning the characters at the indexes of $$$p$$$ are in non-decreasing order.

Formally, if $$$0 \lt i \lt |s|$$$ then $$$s[p[i]]\geq s[p[i-1]]$$$.

Small detour

If $$$|s|,|t|\geq 2$$$, $$$s \lt t$$$ and $$$t[1...|t|-1] \lt s[1...|s|-1]$$$ then $$$s[0] \lt t[0]$$$.

Proof: otherwise we'd get that $$$s[0]\geq t[0]$$$ but $$$s[0]\leq t[0]$$$ by basic property therefore $$$s[0]=t[0]$$$ but $$$t[1...|t|-1] \lt s[1...|s|-1]$$$ which implies that $$$t \lt s$$$ which is a contradiction.

Interesting property 1

If we have two indices $$$i,j$$$ such that $$$0\leq j \lt i \lt |s|$$$ and $$$p[i],p[j] \lt |s|-1$$$, using the third point from the reminders and the above property, we get that if $$$inv[p[j]+1] \gt inv[p[i]+1]$$$ then $$$s[p[i]] \gt s[p[j]]$$$.

Formally, if $$$0 \lt i \lt |s|$$$ and $$$p[i] \lt |s|-1$$$ then $$$s[p[i]] \gt \max\limits_{0\leq j \lt i,\text{ }p[j] \lt |s|-1,\text{ }inv[p[j]+1] \gt inv[p[i]+1]}s[p[j]]$$$.

Interesting property 2

If $$$|s|=n$$$ and $$$i \gt 0$$$ such that $$$p[i]=n-1$$$ ($$$p[i-1] \lt n-1$$$ because $$$p$$$ is a permutation of $$$0,1,...,n-1$$$), using the basic property and the definition, we get that $$$s[n-1] \gt s[p[i-1]]$$$.

Proof: otherwise we'd get that $$$s[n-1]\leq s[p[i-1]]$$$ but $$$s[n-1]\geq s[p[i-1]]$$$ by basic property therefore $$$s[n-1]=s[p[i-1]]$$$ meaning $$$s[p[i-1]...n-1]=s[n-1]+s[p[i-1]+1...n-1]\implies s[n-1]$$$ is a proper prefix of $$$s[p[i-1]...n-1]$$$ so $$$s[p[i]...n-1] \lt s[p[i-1]...n-1]$$$ which is a contradiction.

Formally, if $$$0 \lt i \lt n$$$ and $$$p[i]=n-1$$$ then $$$s[p[i]] \gt s[p[i-1]]$$$

Inverse Suffix Array

We are given the suffix array $$$p$$$ of a string $$$s$$$ which we have to reconstruct.

We will set $$$s[p[i]]$$$ to c for every $$$i=0,1,...,n-1$$$ with c being non-decreasing as $$$i$$$ increases.

c will be initialized as 'a' as it gives the most chances to increment it, and if it has to exceed 'z', then there is no valid string. Note that it's useless here to increment c by more than one letter difference at once.

When to increment c

  • $$$i \gt 0$$$ and $$$p[i]=n-1$$$.
  • $$$p[i] \lt n-1$$$ and there is some $$$j \lt i$$$ such that $$$p[j] \lt n-1$$$, $$$inv[p[j]+1] \gt inv[p[i]+1]$$$ and $$$s[p[j]]$$$ has current value of c.

The second part can be done using a $$$\max$$$ char Segment tree or Fenwick tree. We initialize the tree with any character that's < 'a', and then every time after setting $$$s[p[i]]$$$ to c with $$$p[i] \lt n-1$$$, we also set $$$tree[inv[p[i]+1]]$$$ to c. So that way, at any other $$$i' \gt i$$$ such that $$$p[i'] \lt n-1$$$, we can check if $$$\max(tree[inv[p[i']+1]+1...n - 1])$$$ is c meaning the second case is met so we have to increment it.

  • Vote: I like it
  • +35
  • Vote: I do not like it

| Write comment?
»
10 months ago, hide # |
← Rev. 4  
Vote: I like it +1 Vote: I do not like it

Well written editorial! One thing though: I may be wrong but I don't believe that a segment tree/fenwick tree is required at all. The first case of p[i] = n-1 is correct. For the second, I believe that since we are processing p[i] in order i = 0, ..., n-1, if inv[p[i-1] + 1] > inv[p[i] + 1] (the suffix minus the first character of the previous processed suffix and the current suffix) then we need to increase the character $$$c$$$, otherwise we can just set s[p[i]] to $$$c$$$ as the suffix. This will ensure the ordering is correct. I got an accepted solution with this approach, and much simpler:

solution