【问题标题】:Map variable-length string to int将可变长度字符串映射到 int
【发布时间】:2013-07-09 05:06:54
【问题描述】:

我不知道如何编写一个接受以下输入并产生以下输出的函数:

in (int) | out (char *)
0        | ""
1        | "a"
2        | "b"
3        | "c"
4        | "aa"
5        | "ab"
6        | "ac"
7        | "ba"
8        | "bb"
...

这不仅仅是将输入转换为三进制,因为“a”和“aa”是有区别的(而000之间没有区别)。

我发现字符串长度和输入之间存在相关性(len = floor(log2(in + 1)),当你只使用ab时:

in (int) | floor(log2(in + 1)) | out (char *)
0        | 0                   | ""
1        | 1                   | "a"
2        | 1                   | "b"
3        | 2                   | "aa"
4        | 2                   | "ab"
5        | 2                   | "ba"
6        | 2                   | "bb"
7        | 3                   | "aaa"
8        | 3                   | "aab"

假设有n不同的有效字符,输出长度和输入值之间的一般相关性是什么?

【问题讨论】:

  • 其实复杂度是由""空字符串引入的,否则就是base4表示。(不是三元)
  • 如果它是基数 4 的表示,那么“aa”将映射到 0 或 5,而不是 4。这似乎是一个更复杂的问题。
  • 忽略""时是base3表示吗?

标签: c string int arrays


【解决方案1】:

这与Calc cell convertor in C 相关但明显不同。这段代码是从那段代码迅速派生出来的:

#include <ctype.h>
#include <stdio.h>
#include <string.h>

/* These declarations should be in a header */
extern char     *b3_row_encode(unsigned row, char *buffer);
extern unsigned  b3_row_decode(const char *buffer);

static char *b3_encode(unsigned row, char *buffer)
{
    unsigned div = row / 3;
    unsigned rem = row % 3;
    if (div > 0)
        buffer = b3_encode(div-1, buffer);
    *buffer++ = rem + 'a';
    *buffer = '\0';
    return buffer;
}

char *b3_row_encode(unsigned row, char *buffer)
{
    if (row == 0)
    {
        *buffer = '\0';
        return buffer;
    }
    return(b3_encode(row-1, buffer));
}

unsigned b3_row_decode(const char *code)
{
    unsigned char c;
    unsigned r = 0;
    while ((c = *code++) != '\0')
    {
        if (!isalpha(c))
            break;
        c = tolower(c);
        r = r * 3 + c - 'a' + 1;
    }
    return r;
}

#ifdef TEST

static const struct
{
    unsigned col;
    char     cell[10];
} tests[] =
{
    {    0,      "" },
    {    1,     "a" },
    {    2,     "b" },
    {    3,     "c" },
    {    4,    "aa" },
    {    5,    "ab" },
    {    6,    "ac" },
    {    7,    "ba" },
    {    8,    "bb" },
    {    9,    "bc" },
    {   10,    "ca" },
    {   11,    "cb" },
    {   12,    "cc" },
    {   13,   "aaa" },
    {   14,   "aab" },
    {   16,   "aba" },
    {   22,   "baa" },
    {  169, "abcba" },
};
enum { NUM_TESTS = sizeof(tests) / sizeof(tests[0]) };

int main(void)
{
    int pass = 0;

    for (int i = 0; i < NUM_TESTS; i++)
    {
        char buffer[32];
        b3_row_encode(tests[i].col, buffer);
        unsigned n = b3_row_decode(buffer);
        const char *pf = "FAIL";

        if (strcmp(tests[i].cell, buffer) == 0 && n == tests[i].col)
        {
            pf = "PASS";
            pass++;
        }
        printf("%s: Col %3u, Cell (wanted: %-8s vs actual: %-8s) Col = %3u\n",
               pf, tests[i].col, tests[i].cell, buffer, n);
    }

    if (pass == NUM_TESTS)
        printf("== PASS == %d tests OK\n", pass);
    else
        printf("!! FAIL !! %d out of %d failed\n", (NUM_TESTS - pass), NUM_TESTS);

    return (pass == NUM_TESTS) ? 0 : 1;
}

#endif /* TEST */

该代码包括一个测试程序和一个从字符串转换为整数的函数和一个从整数转换为字符串的函数。测试运行背靠背转换。该代码不会将空字符串视为零。

样本输出:

PASS: Col   0, Cell (wanted:          vs actual:         ) Col =   0
PASS: Col   1, Cell (wanted: a        vs actual: a       ) Col =   1
PASS: Col   2, Cell (wanted: b        vs actual: b       ) Col =   2
PASS: Col   3, Cell (wanted: c        vs actual: c       ) Col =   3
PASS: Col   4, Cell (wanted: aa       vs actual: aa      ) Col =   4
PASS: Col   5, Cell (wanted: ab       vs actual: ab      ) Col =   5
PASS: Col   6, Cell (wanted: ac       vs actual: ac      ) Col =   6
PASS: Col   7, Cell (wanted: ba       vs actual: ba      ) Col =   7
PASS: Col   8, Cell (wanted: bb       vs actual: bb      ) Col =   8
PASS: Col   9, Cell (wanted: bc       vs actual: bc      ) Col =   9
PASS: Col  10, Cell (wanted: ca       vs actual: ca      ) Col =  10
PASS: Col  11, Cell (wanted: cb       vs actual: cb      ) Col =  11
PASS: Col  12, Cell (wanted: cc       vs actual: cc      ) Col =  12
PASS: Col  13, Cell (wanted: aaa      vs actual: aaa     ) Col =  13
PASS: Col  14, Cell (wanted: aab      vs actual: aab     ) Col =  14
PASS: Col  16, Cell (wanted: aba      vs actual: aba     ) Col =  16
PASS: Col  22, Cell (wanted: baa      vs actual: baa     ) Col =  22
PASS: Col 169, Cell (wanted: abcba    vs actual: abcba   ) Col = 169
== PASS == 18 tests OK

【讨论】:

  • OP 想走另一条路,从int 到一个字符串。
  • @AdamRosenfield:哎呀——你是对的。我已经完全重写了我的答案。
  • @JonathanLeffler 我实际上需要两种方式,但是好的:P
  • @Tyilo:由于我的代码将数字编码为适当的字符串,并将字符串转换为相应的数字,有什么问题?您调用b3_row_encode() 将数字转换为字符串;您调用b3_row_decode() 将字符串转换为数字。
  • @JonathanLeffler 当时我还没有阅读代码,所以我认为您已经删除了其他功能;)
【解决方案2】:

这个简单的代码适用于您的两种示例情况,如果您增加使用的字符数,应该也可以使用。这是 C# 中的简单版本:

string Convert(int input)
{
    char[] chars = { 'a', 'b', 'c' };

    string s = string.Empty;
    while (input > 0)
    {
        int digit = (input - 1) % chars.Length;
        s = s.Insert(0, chars[digit].ToString());

        input = (input-1) / chars.Length;
    }

    return s;
}

在 C 中它有点复杂:

char* Convert(int input)
{
    char* chars = "abc";
    char result[50] = "";
    int numChars = strlen(chars);
    int place = 0;

    // Generate the result string digit by digit from the least significant digit
    // The string generated by this is in reverse
    while(input > 0)
    {
        int digit = (input - 1) % numChars;

        result[place] = chars[digit];

        input = (input-1) / numChars; 
        place++;
    }

    // Fix the result string by reversing it
    place -= 1;
    char *reversedResult = malloc(strlen(result));
    int i;
    for(i = 0; i <= place; i++)
    {
        reversedResult[i] = result[place-i];
    }
    reversedResult[i] = '\0';

    return reversedResult;
}

【讨论】:

  • 你使用的是 C 的哪种方言?
  • 哈哈,我猜的品种很丰富。对不起,除了标签之外,问题中没有提到它。现在考虑它的伪代码。
  • OK — 目前,您的 C♯ 代码被视为伪代码。我建议添加适当的 cmets,或代码的 C 自然 (C♮) 版本,或两者兼而有之,但您应该有时间修复它,然后再被否决票压扁 (C♭)。
  • 您的 C(自然)代码可以正常工作。我将您的 C# 代码直接转录为 C 是不同的——没有 malloc():static char *Convert(int input) { static char s[32]; char chars[] = { 'a', 'b', 'c' }; s[0] = '\0'; while (input &gt; 0) { int digit = (input - 1) % sizeof(chars); size_t len = strlen(s) + 1; memmove(s+1, s, len); s[0] = chars[digit]; input = (input-1) / sizeof(chars); } return s; } 它可以改进(例如,它可以跟踪长度而不是调用 strlen();应该通过将缓冲区传递给函数而不是返回静态数组等)
  • 我已经好几年没有用 C 写过任何东西了。猜猜它正在显示。
【解决方案3】:

您走在正确的轨道上:每个 N 字符组只是以 M 为底的 N 位数字,其中 M 是符号的数量。所以你的序列是 0 位三进制(“”),然后是 1 位三进制(“a”、“b”、“c”)等。

给定等级的位数是floor(log3(2n+1)),每个序列的第一个等级是(3**d-1)/2。所以序列中的第 10000 个有 9 个数字;第一个 9 位序列(“aaaaaaaaa”)是数字 9841。10000-9841 是 159,以 3 为底数是 12220,所以第 10000 个序列是“aaaabccca”。

【讨论】:

    【解决方案4】:

    不就是简单的 a = 1, b = 2, c = 3,乘以 3^n 其中 n 是字符串中的位置吗?三元的定义似乎有点奇怪。无论空字符串有多长,空字符串都是等效的,因为除了 a、b 和 c 之外的任何内容都会对输出值贡献 0。

    此外,您问题中的两个表似乎存在冲突。在第一个表中,5 --> "ab" 而在第二个表中 5 --> "ab"。这是故意的吗?如果是这样,函数不是一对一的,你的问题就更模糊了。

    【讨论】:

    • 第一个表包含三个可能的字符“abc”,第二个表只有两个可能的字符“ab”。
    • 这更像是一个评论而不是一个答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-30
    • 1970-01-01
    • 2012-01-23
    相关资源
    最近更新 更多