<p>Given a string <code>s</code>, consider all <em>duplicated substrings</em>: (contiguous) substrings of s that occur 2 or more times.&nbsp;The occurrences&nbsp;may overlap.</p>

<p>Return <strong>any</strong> duplicated&nbsp;substring that has the longest possible length.&nbsp;If <code>s</code> does not have a duplicated substring, the answer is <code>&quot;&quot;</code>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<pre><strong>Input:</strong> s = "banana"
<strong>Output:</strong> "ana"
</pre><p><strong class="example">Example 2:</strong></p>
<pre><strong>Input:</strong> s = "abcd"
<strong>Output:</strong> ""
</pre>
<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>2 &lt;= s.length &lt;= 3 * 10<sup>4</sup></code></li>
	<li><code>s</code> consists of lowercase English letters.</li>
</ul>

'''
All duplicated substrings
A substring is just a prefix of some suffix
finding the longest one that occurs twice can be solve with a suffix array
this is a known problem
'''


class Solution:
    def longestDupSubstring(self, s: str) -> str:
        n = len(s)
        sfx = [((0, 0), 0) for _ in range(n)]
        rank = list(map(ord, s))
        count = [0] * max(300, n)
        k = 1
        while k < n:
            for i in range(n):
                key = (rank[i], rank[i + k] if i + k < n else -1)
                sfx[i] = (key, i)
            sfx.sort(key = lambda x : x[0])
            for i in range(n):
                prev_equal = i > 0 and sfx[i][0] == sfx[i - 1][0]
                rank[sfx[i][1]] = rank[sfx[i - 1][1]] if prev_equal else i
            k *= 2

        ans = (0, 0)
        k = 0
        for i in range(n):
            if rank[i] + 1 == n:
                k = 0
                continue
            j = sfx[rank[i] + 1][1]
            while i + k < n and j + k < n and s[i + k] == s[j + k]:
                k += 1
            ans = max(ans, (k, i))
            if k > 0:
                k -= 1
        return s[ans[1]:sum(ans)]
        
24500