1

我找到了计算字符串中字符频率的代码,但是,它们都使用同一行代码并且没有解释它的含义。有人可以启发我吗?

这是一个例子:

int c = 0, count[26] = {0}, x;

while (string[c] != '\0') {
/** Considering characters from 'a' to 'z' only and ignoring others. */

  if (string[c] >= 'a' && string[c] <= 'z') {
     x = string[c] - 'a';
     count[x]++;
  }

我知道循环将遍历字符串直到它到达末尾。我也得到了 if 语句,因为它只将它限制在 a 和 z 之间。但是,我不知道x = string[c] -'a'在做什么,为什么要减去'a'?我也不明白这样做的目的count[26]是什么。

这是我从这里得到这个程序的地方:

https://www.programmingsimplified.com/c-program-find-characters-frequency

任何帮助都会非常感谢。

4

5 回答 5

2

TL;DR利用了 ASCII 表。

该代码仅接受来自ato的字符z

if (string[c] >= 'a' && string[c] <= 'z') 

因此它创建了一个包含 26 个位置(即 )的数组count[26]来存储这些相同字符的频率。以下

   x = string[c] - 'a';

转换 string[c]int; 事实上,它可以用来利用ASCII 表

在此处输入图像描述

根据 ASCII 表,字母 'a' 到 'z' 分别由从到的int值表示。因此,由于 C 中的数组以 开头,我们需要将元素从将返回的值向左移动,即:97112097string[c]

 x = string[c] - 97;

可以表示为

 x = string[c] - 'a';

有了这个技巧if string[c]is 'a' then :

 x = 'a' - 'a';

转换为x = 97 - 97, 然后x = 0; 所以,

count[x]++;count[0]++;

它将数组的位置 0 增加 1,该位置count被“保留”到字母“a”。同样的逻辑适用于从“a”到“z”的所有其他字母。

但是请记住,并引用Eric Postpischil 的话

C 实现中使用的字符代码不一定具有连续的所有字母。ASCII 确实并且非常常见,但标准并不要求。

因此,如果您的编码是 ASCII,则此解决方案将起作用。

于 2021-02-17T21:04:59.647 回答
1

count[26]是频率表。 count[0]是 的出现次数acount[1]b等的出现次数...

count将数组初始化为全零值

count[26] = {0}

虽然不在字符串的末尾。请记住,C 字符串总是以空字符 ( \0) 结尾。

while (string[c] != '\0') {     

判断 string[c] 处的字符是否在 a 和 z 之间

  if (string[c] >= 'a' && string[c] <= 'z') {

将此字符的 ascii 值(将在 97 到 122 之间)标准化为 0 到 25 之间的值 a。在数学表达式中计算时为 97。

     x = string[c] - 'a';

使用x上面计算的值,将其用作count表中的索引,将其中的任何值递增count[x]1。

     count[x]++;

此代码示例中缺少的是c递增 1 以string[c]引用字符串中的下一个字符的位置。

于 2021-02-17T21:07:00.817 回答
0

'a'首先,该代码通过连续表示而取决于字符'z',这很常见但不能保证。

第二,

int c = 0, count[26] = {0}, x;

while (string[c] != '\0') {
/** Considering characters from 'a' to 'z' only and ignoring others. */

  if (string[c] >= 'a' && string[c] <= 'z') {
     x = string[c] - 'a';
     count[x]++;
  }

有几个问题,我会说更清楚

#include <ctype.h>

// multiple variables on one line is ***very*** bug prone
// so don't do it
int c = 0;
int count[26] = {0};

// set the input - unsigned is important
unsigned char *string = ...;

// loop until the character pointed at by string is '\0'
while ( *string )
{
  // islower() returns non-zero only if the unsigned char value
  // passed is a lower-case letter.
  //
  // If the input int value  can't be represented as an unsigned
  // char the results are undefined behavior (because tolower() 
  // actually takes an int argument.)
  //
  // signed char will be sign-extended when passed as an int argument
  if ( islower( *string ) )
  {
     // get the "index" of the lower-case letter
     // a -> 0, b -> 1, z -> 25
     // depends on a-z being consecutive - not necessarily true
     int x = *string - 'a';

     // increment the number of times this lower-case character
     // is in this string
     count[x]++;
  }

  // go to the next character in the string
  string++;
}

请注意,我在减少使用的行数方面付出了零努力。将代码塞进更少的行数并没有任何好处,但会使代码更难阅读,因此更容易出错。

计算字符串中字符的更好方法:

#include <limits.h>

void countChars( unsigned char *string )
{
    int counts[ UCHAR_MAX ] = { 0 };

    while ( *string )
    {
        counts[ *string ]++;
        string++;
    }
}

如果要计算小写字符:

#include <limits.h>

void countLowerCaseChars( unsigned char *string )
{
    int counts[ UCHAR_MAX ] = { 0 };

    while ( *string )
    {
        counts[ tolower( *string ) ]++;
        string++;
    }
}
于 2021-02-17T21:18:20.673 回答
0

在 ASCII 数据库中,'a' 到 'z' 具有从 0x61 到 0x7A 的连续数字代码。参看。人ASCII。因此,如果减去 'a' 的值,就会得到 0 到 25 之间的数字。这个数字是count[]表中的索引。

于 2021-02-17T21:04:49.867 回答
0

count[26]是一个由 26 个整数组成的数组,每个整数代表一个小写字母 from 'a'to 'z'from insidestring

count[0]是计数器'a'count[1]是计数器'b'等...

x = string[c] - 'a'计算并分配给x在 找到的 char 的索引 0-25 string[c]。Simplifyng:记住'a'是整数 ascii 值 97 十进制。的减法是将数组所需的'a'所有索引从'a'to减少'z'到从 0 到 25 的值。count[]

于 2021-02-17T21:11:32.567 回答