【发布时间】:2015-09-25 20:32:45
【问题描述】:
如何在不使用任何内置方法或函数且不关心字符大小写的情况下,在恒定时间 (O(1)) 内获取字母表 (1-26) 中字符的数值/位置?
【问题讨论】:
如何在不使用任何内置方法或函数且不关心字符大小写的情况下,在恒定时间 (O(1)) 内获取字母表 (1-26) 中字符的数值/位置?
【问题讨论】:
如果你的编译器支持二进制文字,你可以使用
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。
【讨论】:
添加到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 & ~('a' ^ 'A') ) - 'A' + 1; ...你为什么要写这样的东西?
'a' ^ 'A',大多数编译器应该能够不断折叠。
ch | ('a' ^ 'A') - 'a' + 1.
我们可以得到它们的 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);
【讨论】:
严格来说,在 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] 中的位置,使用从零开始的索引语言更自然。
【讨论】: