回文数问题

问题描述

我在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 方法)

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

情况12345
输入999.99721.07387561234567123.45645699.996541239456
输出1110001821.12387831235321124.42145700.007541240421

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

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 程序,如下所示:


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

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

该网站使用 Ruby 1.9.0,有点过时了。Ruby 目前最新版本是 1.9.1。

总结

这三种语言的运行情况如下表:

语言运行时间内存占用源程序行数
C0.09 秒3.0 MB90
C#0.58 秒22 MB82
Ruby1.54 秒6.6 MB44

可以看出,C 语言最快,而且最省内存。C# 语言比较慢,最费内存。Ruby 语言最慢,内存占用居中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值