My approach takes O(N) time to pre-process the string, then O(|ans array|) to compute the answer.
The pre-process is basically KMP failure table building part, on the entire string except last character. ("abacab"
in your example). In the table returned before, the value for last index in given string (i.e 5
or 'b'
) will be two. This means the maximum prefix that matches with AND ends in 'b' is 2. Now if your last character matches with 3rd char of prefix ('a'
), you have got a suffix equal to prefix. ("aba"
)
KMP stops right there. But you want all the matches. So instead of Maximum match that ends in last char (2 in 'b'
), you need to find ALL the matches with prefix that ends in 'b'. So you keep going in KMP's inner loop, and like above, check for current amount of match that ends in 'b' (which can be zero), if next char equals our last char.
def build_table(pat):
table = [0]*len(pat)
p = 0
for i in range(1,len(pat)):
while p>0 and pat[p]!=pat[i]: #KMP's loop i was talking about
p = table[p-1]
if pat[p]==pat[i]:
table[i] = p+1
p+=1
return table
pat = "abracadabab"
table = build_table(pat[:-1]) #build table for "abracadaba", i.e except last
ans = [] #to store answers
p = len(pat)-1 #last index of table building string i.e 5 or 'b'
while p>0: #the main loop
p = table[p-1]
print(p)
if pat[p]==pat[-1]:
ans.append(p+1)
print(ans)
which for "abacab"
prints [1,3]
, for "abracadabra"
it's [1,4]
. Treat entire length as special case.
(Note the similarity between my while
loop and KMP's loop. If you are still confused, I strong suggest to thoroughly read/understand KMP. It's easy to get a overall idea about that, but deeply understanding is really hard and crucial to answering questions like this.)