8.2.6. The Palindrome-Number Problem

Time: 00:06:56 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides (PDF)

Every computer program is a solution (not the solution) to some problem. Before we begin writing code, we must understand the problem we are attempting to solve and know how to solve it. Simple problems are often solved directly with basic programming operations, but solutions for more complex problems require a sequence of operations. Together, these operations form an algorithm or recipe for solving a specific problem. Some well-known problems have equally well-known algorithmic solutions, but computer scientists must often create algorithms for smaller, more unique problems. The palindrome-number problem is a puzzle that has three parts or sub-problems. Two parts are simple, but solving the third part will require us to develop a new algorithm.

Problem:

Find the smallest positive integer that, when squared, produces a palindrome of at least six digits in length: abccba or abcdcba (where a is not 0).

Solution Outline:

We can decompose the palindrome-number problem as follows:
  1. Generate a consistent list of candidate numbers that might solve the problem. To avoid skipping a potential solution, count sequentially. End the search when a candidate satisfies all the requirements.
    1. Set the first candidate number to 1
    2. Increment the candidate number: number = number + 1
  2. Square the number.
  3. Convert the squared number into a string (e.g., convert the integer 123 into the string "123"), and use string operations to identify the first candidate to satisfy the three problem requirements.
  4. Organize the requirements tests from the easiest/fastest to the hardest/slowest, and stop testing a candidate whenever it fails one of the tests:
    1. The squared number is at least six digits long.
    2. The squared number does not begin or end with a '0'.
    3. The digits of the squared number form a palindrome - identifying a palindrome requires us to develop a new algorithm.

Identifying A Palindrome

Digits and characters are significant when determining if a string is a palindrome. However, the determination does not consider spaces, punctuation, and character-case (upper or lower). Although the potential palindromes arising in the palindrome-number problem only contain digits, we can design a general palindrome-checking algorithm without increasing its complexity by imposing three "reasonable" preconditions on the potential palindrome:

  1. All letters must be the same case (it doesn't matter if they are upper or lower case)
  2. The string must not contain punctuation characters
  3. The string must not contain spaces

The programming examples will include a more general program that deals with the spaces, punctuation, and case, but the following algorithms focus on the simple case. How we think about a problem impacts how we design the solution. Two different solutions, taking two different perspectives, demonstrate this observation. Each perspective leads to a different algorithm. Although the two algorithms seem different, the programs derived from them will contain remarkably similar code fragments.

Algorithm 1

Imagine you will solve the problem without a computer - how would you do it, especially if the string is long? I imagine I could write the string on a whiteboard, then point at and compare the characters at each end. I would keep moving my fingers together as long as the characters matched. The string is a palindrome if I reach the middle without any mismatches. The middle character is irrelevant and skipped if the string has an odd number of characters. Seeing a picture helps to make this clear.

A picture shows three steps in determining if the string 'abcdcba' is a palindrome. In the first or (a) step, one finger points to the left 'a', and another points to the right 'a'. In the second or (b) step, both fingers have moved toward the center: one finger points to the left 'b', and the other points to the right 'b'. In the final step, labeled (c), the fingers have moved inward, and each points to a 'c' in the string.
Identifying a palindrome by looking at pairs of characters. As long as each pair of characters match, we continue to move our fingers together. The first pair of characters, labeled (a), match, but we cannot yet conclude that the string is a palindrome (the next pair may not match). In part (b), we move our fingers closer together, and that pair matches, but we still can't claim that the string is a palindrome. In part (c), we move our fingers together again, and the characters match. If the string has an even number of characters, the loop ends with the fingers adjacent to each other; if the string has an odd number of characters, the loop ignores the middle character because it doesn't affect the results. If the loop ends with the fingers adjacent or separated by a shingle character, the algorithm finds no mismatched characters and reports a palindrome.

Our algorithm must also identify when a string is not a palindrome.

A picture showing two steps of the palindrome detection algorithm processing the string 'axcdcya'. In the first or (a) step, one finger points to the left 'a' while another points to the right 'a'. In the second or (b) step, the fingers have moved inward so that the left finger points to 'x' and the right finger points to 'y'.
Identifying a non-palindrome string. As long as each pair matches, as illustrated in part (a), we continue to move our fingers together. We can conclude that the string is not a palindrome whenever a pair of characters doesn't match, as in part (b). We end the palindrome test at this point so that the program doesn't waste computational time. Furthermore, if we continue comparing characters, the next pair might match again, which might cause us to identify the string as a palindrome incorrectly.

Algorithm 2

If we change our perspective by imagining that the program stores the candidate palindrome in computer memory as a string (either a C-string or an instance of the string class), we can develop a different solution. The very definition of a palindrome is a string that reads the same forwards and backward. Another solution, based on the definition, creates and compares two strings. We copy our candidate string, reverse it, and then compare it to the original string. If the two strings are the same (i.e., equal), then the candidate string is a palindrome; if the two strings are not the same, then the candidate is not a palindrome.

The picture illustrates the four steps for detecting a palindrome by reversing the string 'abcdcba'.
Identifying a palindrome by reversing and comparing strings.
  1. The initial candidate string s
  2. Copy s to a second string r
  3. Reverse r
  4. Compare strings s and r; if they are equal, the original string is a palindrome

Again, it is essential that the algorithm correctly identifies a non-palindrome.

The picture again illustrates the four steps for detecting a palindrome by reversing a string but begins with a different string: 'axcdcya'.
Identifying a non-palindrome string. The operations are the same as in the previous example, but the input differs, making the outcome different.
  1. The initial candidate string s
  2. Copy s to a second string r
  3. Reverse r
  4. Compare strings s and r. In this example, the strings are not the same, implying the original string is not a palindrome
Logic diagram or flowchart of a solution to the palindrome number problem. I.i.number = 1. II. square = number * number. III. s = string(number). IV.i. if (length(s) < 6) goto I.II., else goto IV.ii. IV.ii. Does s begin or end with '0'? If yes, go to I.ii., else go to IV.iii. IV.iii. Is s a palindrome? If yes, print the number and end; else, go to I.ii. I.ii. number = number + 1 and go II.
Palindrome-number problem logic diagram. The Roman numerals coordinate the diagram steps with the solution outline presented at the beginning. Cases are implemented as if-statements and arranged from the shortest to the longest-running. The algorithm rejects a string and restarts when a case determines that a string is not a palindrome. A string must pass all cases before the algorithm decides it is a palindrome.

Designing A Solution

If we view the data as too large to manipulate easily, we develop an algorithm that examines the data in small parts. This situation often occurs when the data is too large to fit in main memory. Our "finger" algorithm demonstrates this view. Although the palindrome string does fit in memory, we imagined it as written on a whiteboard, simulating the difficulty of manipulating the whole string. Alternatively, if the data is small enough to fit in main memory, we can develop algorithms that reorganize the data. Our "reverse" algorithm demonstrates this approach.

Our next step is to design an overall solution for the palindrome-number problem. Forming a palindrome is only one of three constraints the problem imposes on the solution. Any algorithm correctly identifying a palindrome and rejecting a non-palindrome will complete this constraint. A sequence of tests ensures that a candidate number satisfies each constraint or reports a failure and skips the remaining tests. We can only declare that we have found a solution number when all tests are satisfied.

We organize the constraint tests based on a common problem-solving technique called case analysis. So, our approach will systematically generate candidate numbers, square them, convert the numbers to strings, and test each string to determine whether it is a palindrome. The adjacent figure and the palindrome algorithms developed above outline the solution's logic but stop short of implementing a program. One advantage of solving a problem before you begin writing code is that the solution leaves you free to choose the language and the implementation details. Even after choosing C++ as our implementation language, we can still choose which string representation to use. We base our first solution on C-strings.