问题描述
当我输入 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;
}
解决方法
如果我已经理解你想要做什么,那么这些嵌套循环
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;
}
,
简单的解决方案?
这里有一个简单的解决方案,我猜测您可能只想知道它是是还是不是一个panagram所以我把你的函数改成了 boolean 函数:
#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 isPanagram;
char str[60];
fgets(str,60,stdin);
isPanagram = findpan(str);
if (isPanagram)
printf("panagram");
else
printf("not a panagram");
return 0;
}
说明
'a' 到 'z' 表示 Decimal 数字中小写字母的范围,在 ASCII table :
for (i = 'a'; i < 'z'; ++i)
tolower
将 arr[j]
character 转换为小写和,然后将其与 i 进行比较:
if (tolower(arr[j]) == i)
stdbool.h
是为使用 bool
aka boolean:
#include <stdbool.h>
,
将自己限制为纯 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
) 输出
Counter = 26
这仅对输入字符串执行一次传递,然后对 letters
数组执行一次传递。没有嵌套循环,也没有对输入字符串的多次传递。
如果我们假设 8 位字符并且可以在堆栈上临时分配 256 个字节,那么这既可读又紧凑且相当高效:
bool is_pangram (const char* str)
{
char used [256]={0};
for(; *str!='\0'; str++)
{
used[*str]=1;
}
return memchr(&used['a'],26)==NULL; // 26 letters in the alphabet
}
256 字节的零输出似乎效率低下,但主流 x86 编译器在 16 条指令中运行它。此函数也没有假设 'a'
到 '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'],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;
}