1

当我输入The quick brown fox jumps over the lazy dog时,会打印以下程序not a pangram。然而,我预计s将是 26 岁并被printf("pangram")处决。我究竟做错了什么?

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

char findpan(char arr[]) {
    int i, j, count = 0;
    for (i = 0; i < strlen(arr); i++) {
        if (isalpha(arr[i]))
            count++;
    }
    for (i = 0; i < strlen(arr); i++) {
        for (j = i + 1; j < strlen(arr); j++) {
            if (arr[i] == arr[j])
                count--;
        }
    }
    return (count);
}

int main() {
    int s;
    char str[60];
    fgets(str, 60, stdin);
    s = findpan(str);
    if (s == 26)
        printf("pangram");
    else
        printf("not a pangram");
    return 0;
}
4

4 回答 4

1

如果我了解您要做什么,那么这些嵌套循环

for (i = 0; i < strlen(arr); i++) {
    for (j = i + 1; j < strlen(arr); j++) {
        if (arr[i] == arr[j])
            count--;
    }
}

不正确。假设您有字符串“AAA”。所以在前面的循环计数之后将等于 3。

现在,在这些嵌套循环之后,计数将等于 0 而不是 1。即当 i = 0 时,对于 j = 1 和 j = 2,arr[j] 等于 arr[i]。所以计数将减少两次。当 i = 1 然后对于 j = 2 再次 arr[j] = arr[i] 并且计数将再减少一次。

此外,您似乎应该忽略字母的大小写。

我可以建议以下功能实现,如下面的演示程序所示。

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

size_t findpan( const char *s )
{
    size_t count = 0;
    
    for ( const char *p = s; *p; ++p )
    {
        if ( isalpha( ( unsigned char ) *p ) )
        {
            char c = tolower( ( unsigned char )*p );
            
            const char *q = s;
            while ( q != p && c != tolower( ( unsigned char )*q ) ) ++q;
            
            if ( q == p ) ++ count;
        }
    }
    
    return count;
}

int main(void) 
{
    printf( "%zu\n", findpan( "The quick brown fox jumps over the lazy dog" ) );
    
    return 0;
}

程序输出为

26

在不使用指针的情况下,函数可以如下所示

size_t findpan( const char *s )
{
    size_t count = 0;
    
    for ( size_t i = 0; s[i] != '\0'; i++ )
    {
        if ( isalpha( ( unsigned char ) s[i] ) )
        {
            char c = tolower( ( unsigned char )s[i] );
            
            size_t j = 0;
            while ( j != i && c != tolower( ( unsigned char )s[j] ) ) ++j;
            
            if ( j == i ) ++count;
        }
    }
    
    return count;
}
于 2021-06-30T19:40:38.193 回答
0

简单的解决方案?

这是一个简单的解决方案,我猜测您可能只是想知道它还是不是pangram,所以我已将您的函数更改为布尔函数:

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

bool findpan(char arr[]) {
    int i,j;
    for (i = 'a'; i < 'z'; ++i) { // goes through the alphabet
        for (j = strlen(arr); j > 0; j--) // goes through the arr[] 
            if (tolower(arr[j]) == i) // checks if the letter exists
                break; // breaks the inner for-loop if letter found
          
        if (j == 0) // if letter not found
            return false;  
    }
    return true;
}

int main() {
    bool isPangram;
    char str[60];
    
    fgets(str, 60, stdin);
    isPangram = findpan(str);
    
    if (isPangram)
        printf("pangram");
    else
        printf("not a pangram");
    return 0;
}

解释

'a''z'表示十进制数字中小写字母的范围,在ASCII 表中

for (i = 'a'; i < 'z'; ++i) 

tolowerarr[j] char转换为小写然后将其与 i 进行比较:

if (tolower(arr[j]) == i)

stdbool.h介绍用于boolaka bool ean:

#include <stdbool.h>
于 2021-06-30T20:09:56.990 回答
0

将自己限制为纯 ASCII,您可以创建一个简单的数组,每个字母一个元素,每个元素初始化为零。然后遍历字符串,并为每个字母将其转换为数组的索引并增加相应的元素值。

完成输入字符串后,循环遍历数组,并为每个非零值增加一个计数器,然后返回它。

也许是这样的:

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

int main(void)
{
    char input[512];

    if (!fgets(input, sizeof input, stdin))
        return 1;  // Failed to read input

    int letters[26] = { 0 };  // 26 letters in the English alphabet

    for (unsigned i = 0; input[i] != '\0'; ++i)
    {
        if (isalpha(input[i]))
        {
            // Limiting myself to e.g. plain ASCII here
            ++letters[tolower(input[i]) - 'a'];
        }
    }

    // Count the number of non-zero elements in the letters array
    unsigned counter = 0;
    for (unsigned i = 0; i < 26; ++i)
    {
        counter += letters[i] != 0;
    }

    // Print result
    printf("Counter = %d\n", counter);
}

使用您的示例输入 ( The quick brown fox jumps over the lazy dog) 它输出

计数器 = 26

这只会对输入字符串进行一次传递,然后对letters数组进行一次传递。没有嵌套循环,没有多次传递输入字符串。

于 2021-07-01T09:16:59.923 回答
0

如果我们假设 8 位字符并且可以暂时在堆栈上分配 256 个字节,那么这既可读、紧凑又相当高效:

bool is_pangram (const char* str)
{
  char used [256]={0};
  for(; *str!='\0'; str++)
  {
    used[*str]=1;
  }
  return memchr(&used['a'], 0, 26)==NULL; // 26 letters in the alphabet
}

256 字节的清零可能看起来效率低下,但主流的 x86 编译器在 16 条指令中运行它。该函数也不假设与'a'to相邻'z'。要添加对大写的支持,只需这样做used[tolower(*str)]=1;可能会引入大量分支。

测试代码:

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

bool is_pangram (const char* str)
{
  char used [256]={0};
  for(; *str!='\0'; str++)
  {
    used[*str]=1;
  }
  return memchr(&used['a'], 0, 26)==NULL;
}

int main (void) 
{
  const char* test_cases[] = 
  {
    "",
    "hello, world!",
    "the quick brown fox jumps over the lazy dog",
    "the quick brown cat jumps over the lazy dog",
    "junk mtv quiz graced by fox whelps",
    "public junk dwarves hug my quartz fox",
  };

  for(size_t i=0; i<sizeof test_cases/sizeof *test_cases; i++)
  {
    printf("\"%s\" is %sa pangram\n", test_cases[i], is_pangram(test_cases[i])?"":"not ");
  }

  return 0;
}
于 2021-07-01T10:07:21.587 回答