C 找出两个相似或不相似的字符串

C 找出两个相似或不相似的字符串,c,string,C,String,规则: 2个字符串,a和b,它们都由ASCII字符和非ASCII字符(例如,gbk编码的汉字)组成 例如: a = "ab中ef日jkl中本" //non-ASCII chars:'中'(twice), '日'(once), '本'(once) b = "bej中中日" //non-ASCII chars:'中'(twice), '日'(once) c = 'lk日日日' //non-ASCII chars:'日'(3 times, more than twice in a) 根据规则

规则: 2个字符串,a和b,它们都由ASCII字符和非ASCII字符(例如,gbk编码的汉字)组成

例如:

a = "ab中ef日jkl中本"  //non-ASCII chars:'中'(twice), '日'(once), '本'(once)
b = "bej中中日"  //non-ASCII chars:'中'(twice), '日'(once)
c = 'lk日日日'   //non-ASCII chars:'日'(3 times, more than twice in a)
根据规则,b与a相似,但c不同。 我的问题是: 我们不知道a和b中有多少非ASCII字符,可能很多。 所以,为了找出非ASCII字符在a和b中出现的次数,我应该使用哈希表来存储它们的出现时间吗? 以字符串a为例:

[non-ASCII's hash-value]:[times]
     中's hash-val      : 2
     日's hash-val      : 1
     本's hash-val      : 1
检查字符串b,如果我们在b中遇到一个非ASCII字符,则对其进行哈希并检查a的哈希表,如果该字符出现在a的哈希表中,则其出现时间递减1。 如果出现时间小于0(-1),那么我们说b与a不相似

还是有更好的办法

PS: 我逐字节读取字符串,如果字节小于128,则将其作为ASCII字符,否则将其作为非ASCII字符(多字节)的一部分。 这就是我正在做的,以找出非ASCII字符。
是吗?

你问了两个问题:

  • 我们可以使用哈希表计算非ASCII字符吗?回答:当然。在读取字符(而不是字节)时,请检查代码点。对于任何大于127的代码点,将其放入计数哈希表中。也就是说,对于字符c,如果c不在表中,则添加(c,1),如果c已经在表中,则将(c,x)更新为(c,x+1)

  • 有没有更好的方法来解决这个问题,而不是在a中递增计数,在b中递减的方法?如果您的哈希表实现提供了接近O(1)的访问权限,那么我怀疑不会。您只需查看字符串中的每个字符一次,并对每个字符执行哈希表插入或查找、加法或减法,以及对0的检查。对于未排序的字符串,您必须查看两个字符串中的所有字符,因此我认为您给出了最佳解决方案

  • 面试官可能会问你这样的问题:“嗯,如果这些字符串实际上是无法存储在内存中的大型文件,我会怎么做?”或者你会问“字符串是否已排序?因为如果是,我可以更快地完成…”

    但是现在让我们假设弦是巨大的。存储在内存中的唯一内容是哈希表。Unicode只有大约100万个代码点,并且每个代码点都存储一个整数计数,因此即使您从千兆字节大小的文件中获取数据,您的哈希表也只需要大约4MB(或者是这个的一个小倍数,因为这会带来开销)

    在没有任何其他条件的情况下,您的算法很好。预先对字符串进行排序是不好的;它占用更多内存,并且不是线性时间操作

    附录

    由于您最初的注释提到了类型
    char
    ,而不是
    wchar\u t
    ,因此我想我应该展示一个使用宽字符串的示例。看

    希望有帮助

    附录2

    好的,这是一个C程序,它精确地显示了如何通过宽字符串并在字符级别工作:

    这是一个非常短的程序,因此我也将其粘贴到这里:

    #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;
    }
    
    我们能从中学到什么?有几件事:

  • 如果对CJK字符使用纯旧
    字符
    ,则字符串长度将错误
  • 要在C中使用Unicode字符,请使用
    wchar\t
  • 对于宽字符串,字符串文字有一个前导的
    L
  • 在本例中,我定义了一个带有CJK字符的字符串,并使用了
    wchar\u t
    和一个带有
    wcslen
    的for循环。请注意,我使用的是实字符,而不是字节,因此我得到了正确的字符数,即5。现在我打印出每个代码点。在面试问题中,您将查看代码点是否为
    =
    128。我用十六进制显示了它们,文化也是如此,因此您可以查找
    0x7F.:-)

    附录3

    这本书中的一些笔记值得一读。与上面的简单示例相比,字符处理有更多的内容。在下面的评论中,J.F.塞巴斯蒂安给出了一些其他重要的链接


    需要解决的少数几件事情之一是正常化。例如,你的面试官是否关心当给出两个字符串时,一个只包含一个字母,另一个包含一个C,后面跟着一个组合标记CEDILLA,它们是否相同?它们表示相同的字符,但一个使用一个代码点,另一个使用两个。

    除非知道编码,否则无法逐字节读取字符串。如果字符串作为真实的字符串呈现给您,那么编码已经为您完成。你的意思是“一个字符一个字符地读取字符串,如果字符的代码点小于128,那么它就是ASCII。”你的例子假设
    =
    ,但规则只说“大于”,即
    @raytoal,我要做的是:for(int i=0;ichar(与
    wchar\t
    )保证用1个字节表示,但我们都知道Unicode提供了超过一百万个字符,因此某些编码使用超过一个字节来表示字符。现代语言倾向于允许程序员一个字符接一个字符
    #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