Search code examples
phpalgorithmpalindrome

PHP beginner palindrome script


I was working on (for fun) writing a script that would recognize palindromes. So far, I'm successful with "Kayak", "Racecar", "Anna", "A man a plan a canal Panama": yet variations on the latter phrase such as "amanaplana canalpan ama" gives me problems.

As a side note: I do understand that using PCRE would make things a lot easier for me, but I'm not fluent in it and one of my major aims was to understand the algorithm behind checking for palindromes.

 <?php
$word = "amanaplana canalpan ama";

$space = " ";
$word_smallcase = strtolower($word);        

$word_array = str_split($word_smallcase);   

if(in_array($space, $word_array)){      

    for($m = 0; $m<count($word_array); $m = $m + 1){
        if($word_array[$m] == $space)
            unset($word_array[$m]);
    }
}
$count = 0;
$scan_count = -1;
for($i = 0; $i < (count($word_array)/2); $i = $i + 1){

    for($j = count($word_array); $j > (count($word_array)/2); $j = $j - 1){

        if($word_array[$i]==$word_array[$j]){
            $count = $count + 1;
            break;
            }
        }
    $scan_count = $scan_count + 1;

        }
if ($count == $scan_count){
    echo $word." is a palindrome";
}
else{

    echo $word ." is NOT a palindrome";
}


?>

I'd appreciate a response regarding:

  • The identification of the bug I'm having.
  • Recommendations as to how I could possibly improve the code (I'd be happy if I could make things work without resorting to $count or $scan_count which seem, to my eye, relatively amateurish).

Thanks in advance.


Solution

  • There's a few things going on here...

    First, I'm not sure if you're aware that unset'ing the array doesn't actually remove the indices:

    $array = array(0, 1, 2, 3);
    unset($array[2]);
    var_dump($array);
    /* array(3) {
      [0]=>
      int(0)
      [1]=>
      int(1)
      [3]=>
      int(3)
    } */
    

    So you're going to have some undefined offsets when you iterate over the elements in the array. To go one by one, you should use the foreach loop control.

    Another issue lies in the nested for loop here:

    for($i = 0; $i < (count($word_array)/2); $i = $i + 1){
    
        for($j = count($word_array); $j > (count($word_array)/2); $j = $j - 1){
    

    Given "amanaplanacanalpanama", look at what you're doing:

    comparing, step by step (btw, you're off by 1 on the initializer for $j... $word_array[count($word_array)] is pointing at the 'm' in panama, not the 'a'.):

    a eq to a?              j is 22          i is 0
                    scan_count: -1   count: 1
    m eq to a?              j is 22          i is 1
    m eq to m?              j is 21          i is 1
                    scan_count: 0    count: 2
    a eq to a?              j is 22          i is 2
                    scan_count: 1    count: 3
    

    a eq to a is fine, and matches... m is found on the next iteration, but when you get to the next 'a', you're finding the original 'a' at the end of panama...

    As a side note, since you are starting over from the very end every time, it would be horribly inefficient O(n^2) given a sufficiently large string...

    Obligatory solution:

    $word = "amanaplana canalpan ama";
    
    $j = strlen ($word)-1;
    $pal = true;
    
    for ($i = 0; $i < strlen($word)/2; ++$i, --$j) {
    
        // skip spaces
        while ($word[$i] === " ") {$i++;}
        while ($word[$j] === " ") {$j--;}
    
        echo "$word[$i] eq $word[$j]?\n";
        if ($word[$i] !== $word[$j])    {
            $pal = false;
            break;
            }
    }
    
    if ($pal) print "yes"; else print "no";