Solution 8

Main | Arrays | Practice 8 | Solution 8 | Associative Arrays | Practice 9 | Solution 9 | Regular Expressions | Practice 10 | Solution 10 | More Regular Expressions | Practice 11 | Solution 11

The solution to Practice 8 is:

<?php

function palindrome_old($string="") {
  $i = strlen($string)-1; // avoid \0 termination of PHP strings
  $mirror_string = "";
  while ($i >= 0) {
    $mirror_string .= $string[$i--];
  }
  return ($mirror_string == $string);
}

function palindrome($string="") {
  $i = 0;
  $j = strlen($string)-1;
  while (floor($j/2) >= 0) {
    if ($string[$i++] != $string[$j--]) return 0;
  }
  return 1;
}

// main()

$array_of_strings = array("racecar", "linux");

for ($i = 0; $i < count($array_of_strings); $i++) {
  if (palindrome($array_of_strings[$i])) {
    print "<p>\"$array_of_strings[$i]\" is a palindrome";
  }
  else {
    print "<p>\"$array_of_strings[$i]\" is not a palindrome";
  }
}

?>

Note in the above example that I have defined two palindrome functions. Both functions work, but palindrome_old is a little less efficient.

The first function creates a string $mirror_string by going through each character within $string backwards so that in the end $mirror_string is $string spelled backwards. It then compares the two strings to see if they are the same. The is a valid way to check for a palindrome, but there is another way to do it.

Let's break a palindrome into an array:

0 1 2 3 4 5 6
r a c e c a r

Note that from the above array that we can derive the following relation (suppose that the above array was called $a):

$a[0] is the same as $a[6]
$a[1] is the same as $a[5]
$a[2] is the same as $a[4]
$a[3] and $a[3] are the same.

The above relation could be generalized for any palindrome:

$i = 0;
$n = count($a);
$a[$i] is the same as $a[$n]
$a[$i+1] is the same as $a[$n-1]
$a[$i+2] is the same as $a[$n-2]
...
until you reach the middle.

The middle of the array could also be defined in a general way:

$mid = floor(count($a)/2);
$a[$mid]

In the above example the PHP function floor is used round down the fraction that will occur when you divide the length of a palindrome in half. (Note that almost all English palindromes seem have an odd numbered length given that all of their characters are symmetric save the middle character. When the set of symmetric characters, which are necessarily even, are added to the one center character an odd number must result.)

Using this idea we can put a pointer on the front and last element and then move through the array with one loop checking for differences. When we find a difference we can immediately declare that the string is not a palindrome. If we don't find a difference then we know that it is a palindrome.

Using the second method is quicker since we have to evaluate less. Imagine if we passed the string "linux" to the first palindrome function. The function would first traverse the entire string to create a mirror string. It would then have to compare if "linux" and "xunil" were the same string, and I imagine that PHP uses a similar method of looping over a string character by character when comparing strings. In contrast the second method starts by comparing "l" to "x" and sees that they are not equal then stops.

The first solution is all that is required for this exercise so if you used the first solution then that is sufficient. I have included an analysis of how to get optimized code to expose you to new ways to think about your programming. A OK programmer can write code that works. A good programmer will write code that works and solves the problem in the most efficient way. Knowing how to solve a problem in the most efficient way involves the study of algorithms which is beyond the scope of this class. However, it is good to develop intuitions about how to program efficiently so you can be aware of how other programmers might evaluate your code. If you end up programming a large PHP project this type of thinking will become more important because you will want to avoid browser timeouts.


jfulton [at] member.fsf.org
22 Aug 2013