【问题标题】:How do I get the numerical value/position of a character in the alphabet (1-26) in constant time (O(1)) without using any built in method or function?如何在不使用任何内置方法或函数的情况下以恒定时间 (O(1)) 获取字母表 (1-26) 中字符的数值/位置?
【发布时间】:2015-09-25 20:32:45
【问题描述】:

如何在不使用任何内置方法或函数且不关心字符大小写的情况下,在恒定时间 (O(1)) 内获取字母表 (1-26) 中字符的数值/位置?

【问题讨论】:

    标签: java c++ c algorithm


    【解决方案1】:

    如果你的编译器支持二进制文字,你可以使用

    int value = 0b00011111 & character;
    

    如果不是,您可以使用 31 代替 0b00011111,因为它们是等价的。

    int value = 31 & character;
    

    或者如果你想使用十六进制

    int value = 0x1F & character;
    

    或八进制

    int value = 037 & character;
    

    您可以使用任何方式来表示值 31。

    这是有效的,因为在 ASCII 中,小写值以 011 为前缀,大写为 010,然后是 1-26 的二进制等价物。 通过使用 00011111 的位掩码和 AND 操作数,我们将 3 个最高有效位转换为零。这给我们留下了 00001 到 11010、1 到 26。

    【讨论】:

    • 如果语言不支持二进制字面量,你仍然可以使用 0x1F。
    • 或 31,如我的回答中所述,但是是的,十六进制也可以。几乎任何可以代表 31 的方式都可以。
    • 我没有看到你回答的那部分。我应该小心在深夜发布这个:(
    • 您将 (a-z) 映射到 (0-25)。您必须将值加 1。
    【解决方案2】:

    添加到very good (self) answer of Charles Staal。

    假设以下 ascii 编码可以工作。更新自Yves Daoust的善意评论

    int Get1BasedIndex(char ch) {
      return ( ch | ('a' ^ 'A') ) - 'a' + 1;
    }
    

    这将使字符变为大写并更改索引。

    然而,更易读的解决方案 (O(1)) 是:

    int Get1BasedIndex(char ch) {
      return ('a' <= ch && ch <= 'z') ? ch - 'a' + 1 : ch - 'A' + 1;
    }
    

    另一种时间恒定但需要一些额外内存的解决方案是:

    static int cha[256];
    
    static void init() {
      int code = -1;
      fill_n (&cha[0], &cha[256], code);
      code = 1;
      for(char s = 'a', l = 'A'; s <= 'z'; ++s, ++l) {
        cha[s] = cha[l] = code++;
      }
    }
    
    int Get1BasedIndex(char ch) {
      return cha[ch];
    }
    

    【讨论】:

    • return ( ch &amp; ~('a' ^ 'A') ) - 'A' + 1; ...你为什么要写这样的东西?
    • 这比第一次发布的答案花费的时间要长得多。通过添加更多的数学而不只是 1 AND 运算,您还可以从值中检查大小写和 -64 或 -96。
    • @immibis 不使用任何 ascii 代码,我更喜欢 'a' ^ 'A',大多数编译器应该能够不断折叠。
    • 好吧,我问了这个问题并回答了这个问题,以便人们了解 ASCII 表的布局方式以及它为何如此布局。另外,向人们展示按位运算并非无用,它们功能强大且非常有用。
    • @MohitJain:我喜欢你表达“Shift”位掩码的方式。强制转换为小写的时间略短:ch | ('a' ^ 'A') - 'a' + 1.
    【解决方案3】:

    我们可以得到它们的 ASCII 值,然后从起始字符中减去 ASCII(a - 97, A - 65)

     char ch = 'a';
        if(ch >=65 && ch <= 90)//if capital letter
            System.out.println((int)ch - 65);
        else if(ch >=97 && ch <= 122)//if small letters
            System.out.println((int)ch - 97);
    

    【讨论】:

      【解决方案4】:

      严格来说,在 C/C++ 中不能移植,因为不能保证字符的顺序。

      这就是说,有一个连续的序列,Char - 'a' 和Char - 'A' 显然给你一个小写或大写字母的位置,你可以写

      Ord= 'a' <= Char && Char <= 'z' ? Char - 'a' : 
          ('A' <= Char && Char <= 'Z' ? Char - 'A' : -1);
      

      如果您希望效率高于安全性,请利用 ASCII 码的二进制表示并使用无分支

      #define ToUpper(Char) (Char | 0x20)
      Ord= ToUpper(Char) - 'a';
      

      (非字母字符的输出被认为是未指定的)。

      与规范相反,这些 sn-ps 返回范围 [0, 25] 中的位置,使用从零开始的索引语言更自然。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-11-04
        • 1970-01-01
        • 2017-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多