你问了两个问题:
我们能使用哈希表计算非ASCII字符吗?答案:可以。当你读取字符(不是字节)时,检查代码点。对于任何代码点大于127的字符,将其放入计数哈希表中。即对于字符c,如果c不在表中,则添加(c,1),如果c已经在表中,则将(c,x)更新为(c,x + 1)。
有没有比你递增a并在遍历b时递减更好的解决方法?如果你的哈希表实现几乎具有O(1)访问权限,那么我认为没有。你正好一次查看字符串中的每个字符,并且对于每个字符,你要执行哈希表插入或查找,加法或减法以及针对0的检查。对于未排序的字符串,你必须无论如何查看两个字符串中的所有字符,因此,我认为你已经给出了最佳解决方案。
面试官可能会希望你说出像这样的话:“嗯,如果这些字符串实际上是无法适应内存的大型文件,我该怎么办?”或者让你问:“那么这些字符串是否已排序?因为如果已排序,我可以更快地完成...”。
但现在假设这些字符串是巨大的。你在内存中存储的唯一东西是哈希表。Unicode只有大约100万个代码点,你为每个代码点存储一个整数计数,因此即使从千兆字节大小的文件获取数据,你只需要大约4MB左右的哈希表(或这个值的小倍数,因为会有开销)。
在没有其他条件的情况下,你的算法很好。预先对字符串进行排序不好;它会占用更多的内存,并且不是线性时间操作。
补充说明:
由于您最初的评论提到了类型char而不是wchar_t,我想展示使用宽字符串的示例。请参见
http://codepad.org/B3MXOgqc。
希望能帮到您。
第二个补充:
好的,这里有一个C程序,可以准确地演示如何逐个字符遍历宽字符串:
http://codepad.org/QVX3QPat
这是一个非常简短的程序,因此我也会在这里粘贴它:
#include <stdio.h>
#include <string.h>
#include <wchar.h>
char *s1 = "abd中日";
wchar_t *s2 = L"abd中日";
int main() {
int i, n;
printf("length of s1 is %d\n", strlen(s1));
printf("length of s2 using wcslen is %d\n", wcslen(s2));
printf("The codepoints of the characters of s2 are\n");
for (i = 0, n = wcslen(s2); i < n; i++) {
printf("%02x\n", s2[i]);
}
return 0;
}
输出:
length of s1 is 9
length of s2 using wcslen is 5
The codepoints of the characters of s2 are
61
62
64
4e2d
65e5
我们可以从中学到什么?以下是几点:
- 如果您在 CJK 字符中使用普通的
char
,则字符串长度将会错误。
- 要在 C 中使用 Unicode 字符,请使用
wchar_t
。
- 字符串字面量在宽字符串前有一个前导
L
。
在此示例中,我使用了带有 CJK 字符的字符串,并使用了
wchar_t
和带有
wcslen
的 for 循环。请注意,我正在处理实际字符,而不是字节,因此我得到了正确的字符计数,即 5。现在,我打印出每个代码点。在您的面试问题中,您将查看代码点是否为
>=
128。我用十六进制显示它们,这是文化习惯,所以您可以寻找
>
0x7F。 :-)
补充 3
值得一读的是http://tldp.org/HOWTO/Unicode-HOWTO-6.html中的一些注释。字符处理比上面的简单示例还要复杂得多。在下面的评论中,J.F. Sebastian提供了许多其他重要的链接。
需要解决的少数问题之一是规范化。例如,当给出两个字符串时,一个仅包含Ç,另一个包含C后跟组合标记CEDILLA BELOW,您的面试官是否关心它们是否相同?它们表示相同的字符,但一个使用一个代码点,另一个使用两个代码点。
>=
,但规则只是说“更多”,即>
。 - jfs