15

Here is a pretty interesting interview question:

Given a word, append the fewest number of letters to it to convert it into a palindrome.

For example, if "hello" is the string given, the result should be "hellolleh." If "coco" is given, the result should be "cococ."

One approach I can think of is to append the reverse of the string to the end of the original string, then try to eliminate the extra characters from the end. However, I can't figure out how to do this efficiently. Does anyone have any ideas?

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
brut3f0rc3
  • 1,173
  • 4
  • 13
  • 19

4 Answers4

10

Okay! Here's my second attempt.

The idea is that we want to find how many of the characters at the end of the string can be reused when appending the extra characters to complete the palindrome. In order to do this, we will use a modification of the KMP string matching algorithm. Using KMP, we search the original string for its reverse. Once we get to the very end of the string, we will have as much a match as possible between the reverse of the string and the original string that occurs at the end of the string. For example:

HELLO
    O

1010
 010

3202
 202

1001
1001

At this point, KMP normally would say "no match" unless the original string was a palindrome. However, since we currently know how much of the reverse of the string was matched, we can instead just figure out how many characters are still missing and then tack them on to the end of the string. In the first case, we're missing LLEH. In the second case, we're missing 1. In the third, we're missing 3. In the final case, we're not missing anything, since the initial string is a palindrome.

The runtime of this algorithm is the runtime of a standard KMP search plus the time required to reverse the string: O(n) + O(n) = O(n).

So now to argue correctness. This is going to require some effort. Consider the optimal answer:

   | original string | | extra characters |

Let's suppose that we are reading this backward from the end, which means that we'll read at least the reverse of the original string. Part of this reversed string extends backwards into the body of the original string itself. In fact, to minimize the number of characters added, this has to be the largest possible number of characters that ends back into the string itself. We can see this here:

   | original string | | extra characters |
           | overlap |

Now, what happens in our KMP step? Well, when looking for the reverse of the string inside itself, KMP will keep as long of a match as possible at all times as it works across the string. This means that when the KMP hits the end of the string, the matched portion it maintains will be the longest possible match, since KMP only moves the starting point of the candidate match forward on a failure. Consequently, we have this longest possible overlap, so we'll get the shortest possible number of characters required at the end.

I'm not 100% sure that this works, but it seems like this works in every case I can throw at it. The correctness proof seems reasonable, but it's a bit hand-wavy because the formal KMP-based proof would probably be a bit tricky.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • Its worked @ templatetypedef. Followed your logic ,and solved the following program "Extend to Palindrome" http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2470 which took 0.028 execution time. – Ritesh Kumar Gupta Jun 16 '12 at 10:59
5

To answer I would take this naive approach:

  1. when we need 0 characters? when string it's a palindrome
  2. when we need 1 character? when except the first character string is a palindrome
  3. when we need 2 characters? when except the 2 start characters the string is a palindrome
  4. etc etc...

So an algorithm could be

  for index from 1 to length
   if string.right(index) is palindrome
    return string + reverse(string.left(index))
   end
  next

edit

I'm not much a Python guy, but a simple minded implementation of the the above pseudo code could be

>>> def rev(s): return s[::-1]
... 
>>> def pal(s): return s==rev(s)
... 
>>> def mpal(s):
...  for i in range(0,len(s)):
...   if pal(s[i:]): return s+rev(s[:i])
... 
>>> mpal("cdefedcba")
'cdefedcbabcdefedc'
>>> pal(mpal("cdefedcba"))
True
CapelliC
  • 59,646
  • 5
  • 47
  • 90
4

Simple linear time solution.

Let's call our string S.

Let f(X, P) be the length of the longest common prefix of X and P. Compute f(S[0], rev(S)), f(S[1], rev(S)), ... where S[k] is the suffix of S starting at position k. Obviously, you want to choose the minimum k such that k + f(S[k], rev(S)) = len(S). That means that you just have to append k characters at the end. If k is 0, the sting is already a palindrom. If k = len(S), then you need to append the entire reverse.

We need compute f(S[i], P) for all S[i] quickly. This is the tricky part. Create a suffix tree of S. Traverse the tree and update every node with the length of the longest common prefix with P. The values at the leaves correspond to f(S[i], P).

aelguindy
  • 3,703
  • 24
  • 31
2

First make a function to test string for palindrome-ness, keeping in mind that "a" and "aa" are palindromes. They are palindromes, right???

If the input is a palindrome, return it (0 chars needed to be added) Loop from x[length] down to x[1] checking if the subset of the string x[i]..x[length] is a palindrome, to find the longest palindrome.

Take the substring from the input string before the longest palindrome, reversing it and adding it to the end should make the shortest palindrome via appending.

coco => c+oco => c+oco+c

mmmeep => mmmee+p => mmmee+p+eemmm

Peter Wishart
  • 11,600
  • 1
  • 26
  • 45
  • what will be thr runtime of this algo – Rajesh M Mar 12 '13 at 12:48
  • I think its n^2 given a linear time test for a palindrome (see [this answer](http://stackoverflow.com/a/1115017/909199)). Can probably be done in linear time by tweaking said linear time algorithm. – Peter Wishart Mar 12 '13 at 14:37