我想检查一个字符是否在字符数组中出现两次,但strchr()会返回第一次出现的字符的位置。

我正在考虑像 Python 那样对数组进行切片arr[3:],从第 3 个元素开始考虑。C 有这样的能力吗?

如果不是,那么这个问题的最佳解决方案是什么?

for loop我要用、if else自己做吗global variables

19

  • 3
    与Python等价的版本arr[3:]&arr[3]arr + 3


    – 

  • 2
    需要明确的是,此切片与原始数组共享内存,它不是像 Python 切片那样的副本。但如果您只是读取数组,而不是修改它,它们是等效的。


    – 

  • 2
    你是在问某个特定字符是否出现了两次?还是任何字符?请举个例子。


    – 

  • 3
    只是为了清楚起见,这是否必须出现两次至少两次


    – 

  • 2
    @BKazimy:在这种情况下,我建议您创建一个arr包含 26 个bool元素的数组并将它们初始化为false。元素arr[0]将指定是否'a'遇到 ,arr[1]将指定是否'b'遇到 ,arr[25]将指定是否'z'遇到 。然后,您可以循环遍历字符串的所有字符,然后相应地设置数组元素。如果true在尝试设置数组元素时已经是 ,那么您就知道您有一个重复的字符。循环遍历所有字符后,您可以验证所有元素是否都是true


    – 



最佳答案
4

的回答,该问题询问如何确定单个字符是否出现多次。同时,问题已更改为询问如何确定任何字符是否出现多次。因此,我在另一个答案中回答了该问题的最新版本。编辑:在撰写本文时,问题已恢复为第 1 版。

strchr()将返回第一个出现的字符的位置。

您可以调用strchr两次。第二次调用将查找第二个字符(如果存在)。以下是示例:

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

bool does_char_exist_twice( const char *str, char c )
{
    const char *p;

    // try to find first occurrence of character, returning
    // false on failure
    p = strchr( str, c );
    if ( p == NULL )
        return false;

    // make p point one past the first occurrence
    p++;

    // try to find second occurrence of character
    p = strchr( p, c );

    // return true if second occurrence found, otherwise false
    return p != NULL;
}

void perform_check_and_print_result( const char *str, char c )
{
    // call the function "does_char_exist_twice" and print the result
    printf(
        "The character '%c' DOES %s exist at least twice in \"%s\".\n",
        c,
        does_char_exist_twice(str, c) ? "   " : "NOT",
        str
    );
}

int main( void )
{
    perform_check_and_print_result( "ab"  , 'a');
    perform_check_and_print_result( "aba" , 'a');
    perform_check_and_print_result( "abc" , 'a');
    perform_check_and_print_result( "abca", 'a');
}

该程序打印以下输出:

The character 'a' DOES NOT exist at least twice in "ab".
The character 'a' DOES     exist at least twice in "aba".
The character 'a' DOES NOT exist at least twice in "abc".
The character 'a' DOES     exist at least twice in "abca".

14

  • 那么,这意味着如果我确定该字符存在一次,我也可以这样做吗?return (strchr(str, strchr(str, c)++)? true: false;


    – 


  • @BKazimy:如果找不到第一个字符,您在评论中发布的代码可能会崩溃。您不能将指针传递NULLstrchr,因为这将调用


    – 


  • 那么除了发现第一个字符为空之外,其他的都可以吗?嘿,我知道第一个字符的位置,所以我可以这样做return strchr(str, c+index)? true: false; ,或者也许只是return strchr(str, c+index);


    – 


  • @BKazimy:您不能++在 的返回值上使用strchr,因为那不是变量。您只能++在变量上使用(或者准确地说,在可修改的+1上使用)。但是,您可以改为这样写。


    – 


  • 1
    @BKazimy:我相信你的意思strchr(str + index + 1, c)是 而不是strchr(str, c+index)。是的,那是正确的。


    – 


的回答,该问题询问如何确定字符串中任何字符是否出现多次。同时,问题已恢复为第 1 次修订,该问题询问如何确定单个字符是否出现多次。请参阅我的其他回答以了解该问题的答案。

解决问题的一个简单但低效的方法是

  • 加载第一个字符,并将该字符与字符串中该字符之后的所有字符进行比较,然后
  • 加载第二个字符,并将该字符与字符串中该字符之后的所有字符进行比较,然后
  • 加载第三个角色并执行相同操作,然后
  • 不断重复,直到到达数组末尾。

以下是一个例子:

// This function will return `true` if any duplicates were found,
// otherwise false.
bool check_for_duplicates_inefficiently( const char *str )
{
    int length = strlen( str );

    for ( int i = 0; i < length; i++ )
    {
        for ( int j = i + 1; j < length; j++ )
        {
            if ( str[i] == str[j] )
                return true;
        }
    }

    return false;
}

更有效的解决方案是创建一个arr包含 26 个bool元素的数组(每个元素代表英文字母表的每个字符),并将其初始化为false。元素arr[0]将指定是否'a'遇到,arr[1]将指定是否'b'遇到,arr[25]将指定是否'z'遇到。

然后您可以循环遍历字符串的所有字符,然后相应地设置数组元素。

如果数组元素在尝试设置时已经为真,则说明存在重复字符。循环遍历所有字符后,可以验证所有元素是否为真true

以下是一个例子:

// This function will return `true` if any duplicates were found,
// otherwise false.
bool check_for_duplicates_efficiently( const char *str )
{
    // initialize array elements to 0/false
    bool letters[26] = {0};

    int length = strlen( str );

    for ( int i = 0; i < length; i++ )
    {
        int c = (unsigned char)str[i];

        if ( isalpha( c ) )
        {
            c = toupper( c );

            int offset = c - 'A';

            if ( letters[offset] )
                return true;

            letters[offset] = true;
        }
    }

    return false;
}

请注意,此答案假设字符串中的所有字符都是英文字母,并且使用默认语言

A还要注意,此解决方案仅适用于字符连续的字符集。 Z就是这种情况,并且大多数字符集都与 ASCII 兼容。 但是,对于一些不常见的字符集(例如 ,情况并非如此。

以下是这两个函数的测试函数:

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

// This function will return `true` if any duplicates were found,
// otherwise false.
bool check_for_duplicates_inefficiently( const char *str )
{
    int length = strlen( str );

    for ( int i = 0; i < length; i++ )
    {
        for ( int j = i + 1; j < length; j++ )
        {
            if ( str[i] == str[j] )
                return true;
        }
    }

    return false;
}

// This function will return `true` if any duplicates were found,
// otherwise false.
bool check_for_duplicates_efficiently( const char *str )
{
    // initialize array elements to 0/false
    bool letters[26] = {0};

    int length = strlen( str );

    for ( int i = 0; i < length; i++ )
    {
        int c = (unsigned char)str[i];

        if ( isalpha( c ) )
        {
            c = toupper( c );

            int offset = c - 'A';

            if ( letters[offset] )
                return true;

            letters[offset] = true;
        }
    }

    return false;
}

void perform_check_and_print_result( const char *str, int spacing )
{
    bool results[2];

    results[0] = check_for_duplicates_inefficiently( str );
    results[1] = check_for_duplicates_efficiently( str );

    spacing = spacing - strlen( str ) + 1;
    if ( spacing < 0 )
    spacing = 0;

    if ( results[0] != results[1] )
    {
        printf(
            "Both functions disagree on whether the string %*c%s\" "
            "contains duplicates.\n",
            spacing,
            '\"',
            str
        );
    }
    else
    {
        printf(
            "Both functions agree that the string %*c%s\" DOES "
            "%s contain duplicate characters.\n",
            spacing,
            '\"',
            str,
            results[0] ? "   " : "NOT"
        );
    }
}


int main( void )
{
    perform_check_and_print_result( "ab"  , 4 );
    perform_check_and_print_result( "aba" , 4 );
    perform_check_and_print_result( "abc" , 4 );
    perform_check_and_print_result( "abca", 4 );
    perform_check_and_print_result( "abcb", 4 );
    perform_check_and_print_result( "abcc", 4 );
    perform_check_and_print_result( "abcd", 4 );
}

该程序的输出如下:

Both functions agree that the string   "ab" DOES NOT contain duplicate characters.
Both functions agree that the string  "aba" DOES     contain duplicate characters.
Both functions agree that the string  "abc" DOES NOT contain duplicate characters.
Both functions agree that the string "abca" DOES     contain duplicate characters.
Both functions agree that the string "abcb" DOES     contain duplicate characters.
Both functions agree that the string "abcc" DOES     contain duplicate characters.
Both functions agree that the string "abcd" DOES NOT contain duplicate characters.

3

  • 嘿@Andreas 我问这个是因为你似乎已经通过了 cs50s 替代任务,除了两个之外我通过了所有检查点::( handles lack of key failed to execute program due to segmentation fault :( handles too many arguments timed out while waiting for program to exit似乎我无法理解,你能启发我吗?


    – 

  • @BKazimy:任务说明如下:"If your program is executed without any command-line arguments or with more than one command-line argument, your program should print an error message of your choice (with printf) and return from main a value of 1 (which tends to signify an error) immediately."——你似乎没有这样做。


    – 


  • 明白了。谢谢你的帮助。


    – 

一个简单的解决方案是结合strchrstrrchr

  • strchr将返回指向该字符第一次出现的指针
  • strrchr将返回指向该字符最后一次出现的指针

如果指针相等,则该字符不存在(两个调用都返回 null)或者只存在一次(两个指针都指向同一个字符)。

#include <string.h>

int char_exists_twice(const char *s, char c) {
    // probably a good idea to check if s is null as well
    const char *first = strchr(s, c);
    const char *last  = strrchr(s, c);

    return first != last;
}

我必须接受所有英文字母用户的输入,并且我应该确保用户没有输入任何字符两次。(不区分大小写)

strchr每次只能找到一个字符。您要查找许多字符。您可以重复调用strchr,但这样做效率低下,因为它必须为每个字符重新扫描字符串。

相反,扫描字符串一次并记录每个字符出现的次数。在 Python 中,你会使用字典,但 C 没有这些。相反,使用足够宽的数组来存储每个字母。

#include <ctype.h>

int first_repeat_letter(const char *string)
{
    // Initialize the count array to all zeros.
    int char_counts[26] = {0};
    for (int i = 0; string[i] != '\0'; i++)
    {
        // Ignore case.
        int c = tolower(string[i]);

        // Ignore non-letters.
        if (!isalpha(c))
        {
            continue;
        }

        // This converts the letters to a number between 0 and 25.
        // 'a' - 'a' = 0, 'b' - 'a' = 1, 'z' - 'a' = 25.
        int idx = c - 'a';
        char_counts[idx]++;

        // Return the index where the duplicate was found.
        if (char_counts[idx] >= 2)
        {
            return i;
        }
    }

    return -1;
}

通过返回索引,调用者可以找到重复的字母。

#include <stdio.h>

char string[] = "abcdef1123";
int i = first_repeat_letter(string);
if (i < 0)
{
    printf("No repeats found in '%s'\n", string);
}
else
{
    printf("Repeat char found in '%s': '%c' at %d\n", string, string[i], i);
}

实际上我希望英文字母的所有字符都出现一次,并且检查是否有重复。

这也可以轻松适应这种情况。迭代char_count并验证每个元素是否为 1。