回文数问题

问题描述

我在2008年6月写了一篇随笔“可以使用C#语言的在线ACM题库”,其中提到 Sphere
Onlile Judge (SPOJ)
 网站。现在我们来看看该网站 SPOJ Problem Set (classical) 中的“5. The Next Palindrome”。这道题目的主要内容如下所示:

The Next Palindrome: A positive integer is called a palindrome if its representation in the decimal system is the same when read from left to right and from right to left. For a given positive
integer K of not more than 1,000,000 digits, write the value of the smallest palindrome larger than K to output. Numbers are always displayed without leading zeros.

Input: The first line contains integer t, the number of test cases. Integers K are given in the next t lines.

Output: For each K, output the smallest palindrome larger than K.

Warning: large Input/Output data, be careful with certain languages

Time limit: 9s

简单地说,就是要找出比指定的数大的最小回文数(palindrome)。问题是这个数可能包含有百万个十进制数字,而时间限制是九秒,所以算法不能太低效。比如用最笨的办法,使用 BigInteger ,从指定的数开始逐个往上加一,然后判断是不是回文数,肯定是会超时的。

C# 语言解答

下面就是求解这个问题的 C# 程序:

01:  using System;
02:  using System.IO;
03:
04:  namespace Skyiv.SphereOnlineJudge
05:  {
06:    // http://www.spoj.pl/problems/PALIN/
07:    class Palindrome
08:    {
09:      static void Main()
10:      {
11:        new Palindrome().Run(Console.In, Console.Out);
12:      }
13:
14:      void Run(TextReader reader, TextWriter writer)
15:      {
16:        for (var i = int.Parse(reader.ReadLine()); i > 0; i--) Out(writer, reader.ReadLine());
17:      }
18:
19:      void Out(TextWriter writer, string s)
20:      {
21:        var n = s.Length / 2;
22:        if (IsWhole9s(s)) Out(writer, s.Length - 1);
23:        else if (s.Length == 1) writer.WriteLine((char)(s[n] + 1));
24:        else if (IsLeftReverseLargerThanRight(s)) Out(writer, s, n, false, s[n]);
25:        else if (s.Length % 2 != 0 && s[n] < ‘9‘) Out(writer, s, n, false, (char)(s[n] + 1));
26:        else Out(writer, s, n - GetLeftSuccessive9sCount(s), true, ‘0‘);
27:      }
28:
29:      void Out(TextWriter writer, int length)
30:      {
31:        writer.Write(‘1‘);
32:        writer.Write(new string(‘0‘, length));
33:        writer.WriteLine(‘1‘);
34:      }
35:
36:      void Out(TextWriter writer, string s, int length, bool isNext, char center)
37:      {
38:        var zeros = new string(‘0‘, s.Length / 2 - length);
39:        var c = (char)(s[length - 1] + (isNext ? 1 : 0));
40:        writer.Write(s.Substring(0, length - 1));
41:        writer.Write(c);
42:        writer.Write(zeros);
43:        if (s.Length % 2 != 0) writer.Write(center);
44:        writer.Write(zeros);
45:        writer.Write(c);
46:        writer.WriteLine(GetLeftReverse(s, length - 1));
47:      }
48:
49:      bool IsWhole9s(string s)
50:      {
51:        foreach (var c in s) if (c != ‘9‘) return false;
52:        return true;
53:      }
54:
55:      bool IsLeftReverseLargerThanRight(string s)
56:      {
57:        var n = s.Length / 2;
58:        var k = s.Length % 2 + n;
59:        for (var i = 0; i < n; i++)
60:        {
61:          var cmp = s[k + i].CompareTo(s[n - 1 - i]);
62:          if (cmp < 0) return true;
63:          if (cmp > 0) return false;
64:        }
65:        return false;
66:      }
67:
68:      char[] GetLeftReverse(string s, int length)
69:      {
70:        var array = new char[length];
71:        for (var i = 0; i < length; i++) array[i] = s[length - 1 - i];
72:        return array;
73:      }
74:
75:      int GetLeftSuccessive9sCount(string s)
76:      {
77:        var i = s.Length / 2 - 1;
78:        while (i >= 0 && s[i] == ‘9‘) i--;
79:        return s.Length / 2 - 1 - i;
80:      }
81:    }
82:  }

在该网站上提交后,运行通过,运行时间是 0.58 秒,占用内存为 22 MB,目前在 C# 语言中排名第三位。

该网站使用的 C# 编译器是 Mono C# compiler version 2.0.1,有点过时了。Mono C# 编译器目前最新版本是 2.6.4。

所用算法

将问题分为以下五种情况(对应 C# 源程序第 22 到 26 行):

  1. 如果所给的数全部由 9 组成,则输出 10…01 ,这里 0 的个数比 9 的个数少一个。(第 22 行,第 49 到 53 行的 IsWhole9s 方法,第 29 到 34 行的 Out 方法)
  2. 否则,如果所给的数只有一位数,则输出下一个数字。(第 23 行)
  3. 否则,如果所给的数的左半边倒转过来大于右半边,则以左半边为基准输出回文数。(第 24 行,第 55 到 66 行的 IsLeftReverseLargerThanRight 方法,第 36 到 47 行的 Out 方法,第 68 到 73 行的 GetLeftReverse 方法)
  4. 否则,如果所给的数的位数是奇数,并且最中间一位数字小于 9 ,则将最中间一位数字加一,并以左半边为基准输出回文数。(第 25 行,第 36 到 47 行的 Out 方法)
  5. 否则,计算所给的数的左半边从低位算起的连续的 9 的个数,这些连续的 9 需要替换为 0 ,然后再将左半边最右侧的非 9 数字加一,以此为基准输出回文数。如果所给的数的位数是奇数,则最中间一位数字必定是 9,这个 9 也必须替换为 0 。(第 26 行,第 75 到 80 行的 GetLeftSuccessive9sCount 方法,第 36 到 47 行的 Out 方法)

除了第一种情况,所求的回文数的位数是和所给的数的位数是一样的。举例如下:

情况 1 2 3 4 5
输入 9 99.99 7 21.07 38756 1234567 123.456 45699.99654 1239456
输出 11 10001 8 21.12 38783 1235321 124.421 45700.00754 1240421

上表中的小数点只是为了标出数的最中间位置,请忽略之。

C 语言解答

将前面的 C# 程序翻译为 C 程序,如下所示:

01:  #include <stdio.h>
02:  #include <stdlib.h>
03:  #include <string.h>
04:  
05:  #define MAX_DIGITS 1000000
06:
07:  static char buf[MAX_DIGITS / 2]; // for getString and getLeftReverse
08:  
09:  const char* getString(int c, int length)
10:  {
11:    memset(buf, c, length);
12:    buf[length] = ‘\0‘;
13:    return buf;
14:  }
15:
16:  const char* getLeftReverse(const char* s, int length)
17:  {
18:    for (int i = 0; i < length; i++) buf[i] = s[length - 1 - i];
19:    buf[length] = ‘\0‘;
20:    return buf;
21:  }
22:
23:  void out(char* s, int len, int length, int isNext, char center)
24:  {
25:    const char* zeros = getString(‘0‘, len / 2 - length);
26:    char c = s[length - 1] + isNext;
27:    s[length - 1] = ‘\0‘;
28:    fputs(s, stdout);
29:    putchar(c);
30:    fputs(zeros, stdout);
31:    if (len % 2 != 0) putchar(center);
32:    fputs(zeros, stdout);
33:    putchar(c);
34:    fputs(getLeftReverse(s, length - 1), stdout);
35:    putchar(‘\n‘);
36:  }
37:
38:  void out101(int length)
39:  {
40:    putchar(‘1‘);
41:    fputs(getString(‘0‘, length / 2), stdout);
42:    fputs(getString(‘0‘, length / 2), stdout);
43:    if (length % 2 != 0) putchar(‘0‘);
44:    putchar(‘1‘);
45:    putchar(‘\n‘);
46:  }
47:
48:  int isWhole9s(const char* s)
49:  {
50:    for (int i = 0; s[i] != ‘\0‘; i++) if (s[i] != ‘9‘) return 0;
51:    return 1;
52:  }
53:
54:  int isLeftReverseLargerThanRight(const char* s, int length)
55:  {
56:    int n = length / 2, k = length % 2 + n;
57:    for (int i = 0; i < n; i++)
58:    {
59:      int cmp = s[k + i] - s[n - 1 - i];
60:      if (cmp < 0) return 1;
61:      if (cmp > 0) return 0;
62:    }
63:    return 0;
64:  }
65:
66:  int getLeftSuccessive9sCount(const char* s, int len)
67:  {
68:    int i = len / 2 - 1;
69:    while (i >= 0 && s[i] == ‘9‘) i--;
70:    return len / 2 - 1 - i;
71:  }
72:
73:  int main()
74:  {
75:    char s[MAX_DIGITS + 1];
76:    int cnt = atoi(fgets(s, MAX_DIGITS, stdin));
77:    while (cnt-- > 0)
78:    {
79:      fgets(s, MAX_DIGITS, stdin);
80:      int len = strlen(s);
81:      s[--len] = ‘\0‘;
82:      int n = len / 2;
83:      if (isWhole9s(s)) out101(len - 1);
84:      else if (len == 1) printf("%c\n", s[0] + 1);
85:      else if (isLeftReverseLargerThanRight(s, len)) out(s, len, n, 0, s[n]);
86:      else if (len % 2 != 0 && s[n] < ‘9‘) out(s, len, n, 0, s[n] + 1);
87:      else out(s, len, n - getLeftSuccessive9sCount(s, len), 1, ‘0‘);
88:    }
89:    return 0;
90:  }

在该网站提交后,运行通过,运行时间是 0.09 秒,占用内存为 3.0 MB,目前在 C99 strict 语言中排名第八位。

该网站使用的 C 编译器是 gcc 4.3.2,有点过时了。gcc 目前最新版本是 4.5.0。

Ruby 语言解答

将前面的 C# 程序翻译为 Ruby 程序,如下所示:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

def is_whole_9s
s

  s.each_char
do |c|

    return false if c
!= ?
9

  end

  true

end

def is_left_reverse_larger_than_right
s

  n
= s.length /
2

  s[0,
n].reverse > s[s.length %
2 +
n, n]

end

def get_left_successive_9s_count
s

  i
= s.length /
2 -
1

  i
-=
1 while i
>=
0 &&
s[i] == ?
9

  s.length
/
2 -
1 -
i

end

def out
s, length, isNext, center

  zeros
=
‘0‘ *
(s.length /
2 -
length)

  prefix
= s[
0,
length -
1]

  c
= s[length -
1]

  c
= c.succ
if isNext

  print
prefix

  print
c

  print
zeros

  print
center
if s.length
%
2 !=
0

  print
zeros

  print
c

  puts
prefix.reverse

end

$stdin.gets

$stdin.each_line
do |s|

  s.chop!

  n
= s.length /
2;

  case

  when is_whole_9s(s)
then puts
"1#{‘0‘
* (s.length - 1)}1"

  when s.length
==
1 then puts
s[
0].succ

  when is_left_reverse_larger_than_right(s)
then out
s, n,
false,
s[n]

  when s.length
%
2 !=
0 &&
s[n] < ?
9 then out
s, n,
false,
s[n].succ

  else out
s, n - get_left_successive_9s_count(s),
true,
?
0

  end

end

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

时间: 2024-10-13 12:28:56

回文数问题的相关文章

判断一个数是否为回文数

#include <stdio.h> int is_palindromic(int num) {  char _old = num;  char _new = 0;  while (num)  {   _new = _new * 10 + (num % 10);   num = num / 10;  }  if (_new == _old)  {   return 1;  }  else  {   return 0;  } } int main() {  int num = 0;  scanf

LeetCode 9 Palindrome Number (回文数)

翻译 确定一个整数是否是回文数.不能使用额外的空间. 一些提示: 负数能不能是回文数呢?(比如,-1) 如果你想将整数转换成字符串,但要注意限制使用额外的空间. 你也可以考虑翻转一个整数. 然而,如果你已经解决了问题"翻转整数(译者注:LeetCode 第七题), 那么你应该知道翻转的整数可能会造成溢出. 你将如何处理这种情况? 这是一个解决该问题更通用的方法. 原文 Determine whether an integer is a palindrome. Do this without ex

要求循环输入一个数,判断是否为回文数

import java.util.Scanner; public class HuiWenShu { public static void main(String[] args) { Scanner input = new Scanner(System.in); char c = 'y'; //初始化c为y,为下面的循环做好准备 while(c == 'y'){ while(c == 'y'){ System.out.println("请随意输入一个大于三位的奇位数"); //回文数属

回文数 第N个回文数

判断回文数还是不难,如果能转为字符串就更简单了. 如果是求第N个回文数呢. 12321是一个回文数,这里先考虑一半的情况. 回文数的个数其实是有规律的.如: 1位回文数: 9个 2位回文数: 9个 3位回文数: 90个 4位回文数: 90个 5位回文数: 900个 6位回文数: 900个 … 我们看到9.90.900,是不是很有规律,那是什么原因?很简单,我们把回文数拆开两半 [123321]来看.两半的变化一样的,那我们只算其中一半就行了.首位不能是0,所以左半最小为 100,最大为999,共

Palindrome Number (回文数)

回文数是指这样的数字:正读和倒读都是一样的.如:595,2332都是回文数,234不是回文数. 注意:负数不是回文数 Determine whether an integer is a palindrome. Do this without extra space. Some hints: Could negative integers be palindromes? (ie, -1) If you are thinking of converting the integer to string

洛谷 P1207 [USACO1.2]双重回文数 Dual Palindromes

题目描述 如果一个数从左往右读和从右往左读都是一样,那么这个数就叫做"回文数".例如,12321就是一个回文数,而77778就不是.当然,回文数的首和尾都应是非零的,因此0220就不是回文数. 事实上,有一些数(如21),在十进制时不是回文数,但在其它进制(如二进制时为10101)时就是回文数. 编一个程序,从文件读入两个十进制数N (1 <= N <= 15)S (0 < S < 10000)然后找出前N个满足大于S且在两种或两种以上进制(二进制至十进制)上是

一个5位数,判断它是不是回文数

题目:一个5位数,判断它是不是回文数.即12321是回文数,个位与万位相同,十位与千位相同. 1 package com.li.FiftyAlgorthm; 2 3 import java.util.Scanner; 4 5 /** 6 * 题目:一个5位数,判断它是不是回文数.即12321是回文数,个位与万位相同,十位与千位相同. 7 * @author yejin 8 */ 9 public class Palindrom { 10 public static void main(Strin

习题-四季-回文数-不死神兔

1-键盘录入月份,输出对应的季节.一年有四季;3,4,5春季;6,7,8夏季;9,10,11秋季;12,1,2冬季 public class Demo02Test {     public static void main(String[] args) {         // 键盘录入一个月份,用Scanner实现         Scanner sc = new Scanner(System.in);         // 接收数据         System.out.println("请

输出1-256之间一个数的平方是回文数

方法: 通过一个函数求出这个数一共是几位数 循环取余数依次放入临时数组 通过数组下标循环判断 1 //功能:打印所有不超过 n( n<256)的其平方具有对称性的数(也称回文数) 2 3 4 5 #include<stdio.h> 6 #include<stdlib.h> 7 8 int judg(int); 9 int getBit(int); //返回整数值的位数 10 11 void main(){ 12 for (int i = 0; i < 256; i++)

9. 回文数 Palindrome Number

Determine whether an integer is a palindrome. Do this without extra space. 题意:判断一个数字是否为回文数 如何取得一个Integer的最高位?假设x = 688答:x / mask. 设置一个和x位数一样的mask,mask = 100,然后用x/mask,表示x里面有几个mask,即是最高位数字. 688里有6个100,即为6. 如何删去一个Integer的最高位?假设x = 688答:x = x % mask. 还是