Function that for a string s, at index i, returns the length of the longest border of s[0..i] (inclusive).
For example, consider the string s=abababcaab.
- at
i=0, we have substrings[0..0]=awhich has no border (border is proper prefix/suffix). Sopr(0) = 0. - at
i=1, we have substrings[0..1]=abwhich has no border, sopr(1) = 0. - at
i=2, we have substrings[0..2]=abawhich hasa..aas border.pr(2) = 1 - at
i=3, we have substrings[0..3]=ababwhich hasab..abas border.pr(3) = 2 - at
i=4, we have substrings[0..4]=ababawhich hasab[a]baas border (that is, the prefix isabaand the suffix isabawhich overlaps).pr(4) = 3. - at
i=5, we have substrings[0..5]=abababwhich hasab[ab]abas border (that is, the prefix isababand the suffix isababwhich overlaps).pr(5) = 4. - at
i=6, we have substrings[0..6]=abababcwhich has no border.pr(6) = 0. - at
i=7, we have substrings[0..7]=abababcawhich has bordera..a.pr(7) = 1. - at
i=8, we have substrings[0..8]=abababcabwhich has borderab..ab.pr(8) = 2.
In toto, the prefix function is:
ababcaab 01234012 § s[0..i] has a border of length at pr(i+1)-1
- That is, given the substring
s[0..i], we can predict thats[0..i]will have some border (perhaps not the longest border) of lengthpr(i+1)-1. - Suppose
s[0..i+1]has longest border of lengthL=pr(i+1)(by definition ofpr). Supposepr(i+1) >= 1. Then I can writes[0..i+1]as:
s[0..i+1] = p[0]p[1]...p[L]|s[L+1]...s[i+1-L-1]|p[0]p[1]...p[L] ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^If I now want to consider s[0..i], I need to drop the last letter p[L], which leaves me with:
s[0..i] = p[0]p[1]...p[L-1]p[L]|s[L+1]...s[i+1-L-1]|p[0]p[1]...p[L-1] ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^- where we have a border of
p[0]...p[L-1]. - There maybe a longer border, involving other terms of
s[0..i]. But we know that the border is at leastpr(i) >= pr(i+1)-1. - Upon re-arranging, we see that
pr(i+1) <= pr(i) + 1. - This tells us that the border can increase by at most 1 (it can drop to zero, no lower bound!). So we have:
0 <= pr(i+1) <= pr(i) + 1. - So if we think of borders of
s[0..i+1], we know that the longest border can be of length of borderpr(i) + 1. All other borders will be of length<= pr(i), so these other borders will be borders ofs[0..i]! - Thus, to move from
s[0..i]tos[0..(i+1)], we simply need to be able to find the longest border ofs[0..(i+1)]. All other borders will come froms[0..i].
§ Lemma: Enumerating borders (what is a good name for this lemma?)
- Think of the longest border
123456:
123456----123456 ^ ^ L N- Now consider a shorter border
ab:
ab3456----1234ab ^ ^ L N- But we must have
a~1andb~2since it's the same string! So the border is reallyab34ab, and the string is:
ab34ab----ab34ab ^ ^ L N- This shows that given the longest border
123456ofs[0:N]which has lengthL, any other border ofs[0:N](such asab) is also a border ofs[0:L]. - Generalizing, given the longest border of
s[0..n]of lengthL, any smaller border ofs[0:N]is a border ofs[0:L].
§ Algorithm to enumerate borders.
All borders of s[0:N] can be enumerated by:
- Taking the longest border of length
Lofs[0:N](given bypr(N)). - considering the border as being the substring
s[0:L](given bypr(L)). - Taking the longest border of
s[0:L] - ... recurse till we hit empty string.
§ Computing pr(i+1)
- We know that
pr(0) = 0. - At
pr(i+1), we know thats[0:pr(i)] = s[-pr(i):-1]
§ Border function is a fractal
Consider a general string which looks like:
abcdef--abcdefIf we think of the second longest border, say 12, we will get that
the string must be:
12cdef--abcd12 ^^ &&But this implies that the occurrence of ef (marked with &&) and the occurrence
of ab (marked with ##) must be 12, 12 respectively. This means that the
string is:
12cd12--12cd12So we started with a full string:
------------------Then matched the left and right (very 1/3 cantor):
------- ------ ----Then matched the left and right of these again and unified the leftovers, finding that it looks like:
-- -- -- -- -- -- ---and so on. Isn't this so cool? Borders of a string are a fractal-like object!