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 c开发者_如何学JAVAode (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.
Currently, you're testing on a word-for-word basis, and that will definitely cause bugs in cases where words are uneven across the palindrome.
The easy way to see if a string is palandromic in PHP:
$test = str_replace( array(',', '.', ' '
//, and any other punctuation you aren't using
), '', $input );
$test = strtolower( $test );
echo $input . ' is ' .
( ( strrev( $test ) == $test )? "": "not " )
' palandromic.';
As to your code: iterating through an array and removing things at the same time is an invitation to trouble. You're better just using str_replace. If that isn't an option, I would use:
// this takes more space, but it is easier to understand.
$tmp = array();
for($m = 0; $m<count($word_array); $m++){ // ++ is the same as $m = $m + 1
if($word_array[$m] != $space)
$tmp[] = $word_array[$m];
}
$word_array = $tmp;
If strrev (reverses a string) is not available:
$l = count( $word_array ) / 2; // cache this, no sense in recounting
$is_palindromic = TRUE;
for( $i = 0; $i < $l; $i++ )
{
if( $word_array[ $i ] != $word_array[ ( -1 - $i ) ] )
{
$is_palindromic = FALSE;
break;
}
}
echo $input . ' is ' .
( $is_palindromic )? "": "not " )
' palandromic.';
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";
pseudocode:
string phrase = "my phrase here"
int i = 0
int j = phrase.length - 1
while i < j:
if phrase[i] is not alphanumeric:
i++;
continue;
if phrase[j] is not alphanumeric:
j--;
continue;
if phrase[i] != phrase[j]
return false;
i++;
j--;
return true
I think it is possible to just remove all the spaces and ignore words completely. Just split into two (or thereabouts if string length is an odd number), reverse any half and then see if they match.
$word = "amanaplana canalpan ama";
$sanitizedWord = preg_replace("'\s+'", '', strtolower($word));
$halfway = strlen($sanitizedWord)/2;
$roundedDownMidPoint = floor($halfway);
$firstHalf = substr($sanitizedWord, 0, $roundedDownMidPoint);
$secondHalf = substr($sanitizedWord, is_float($halfway) ? $roundedDownMidPoint+1 : $halfway);
if ($firstHalf === strrev($secondHalf)) {
echo $sanitizedWord." is a palindrome";
}
Tested with "Kayak", "Racecar", "Anna", "A man a plan a canal Panama" and "amanaplana canalpan ama" which are all identified correctly as palindromes.
<?php
$a="amanaplana canalpan ama";
echo "String entered: " . $a;
$b= preg_replace('/\s+/', '', $a);
$org= strtolower($b);
$chk= strrev($org);
echo "<br/>";
if ($org==$chk)
{ echo "Palindrome"; }
else
{ echo "Not Palindrome"; }
?>
精彩评论