【问题标题】:How to remove duplicate char in string in C如何在C中删除字符串中的重复字符
【发布时间】:2020-08-04 17:53:09
【问题描述】:

每个字符的总和

由于长期隔离,乔乔变得超级无聊。为了摆脱无聊,乔乔提议和比比玩一个游戏。游戏计算单词中每个字符的总数。因为乔乔很想赢,所以他请你帮他猜猜比赛的结果。

格式输入

输入以整数T 开头,即测试用例的数量。 每个测试用例都以包含单个字符串W 的行开头,该字符串表示单词。保证单词只包含字母数字和空格。

格式化输出

每个测试用例都以"Case #X:" 行开头,其中X 代表测试用例。此行之后是单词中每个字符的结果和字符数的总和。结果将首先按字母排序,然后是数字。

约束

1 ≤ |????| ≤ 10000

示例输入

3
Bina Nusantara University
Binus 2020
Corona Virus Covid 19

样本输出

案例#1:

[A] => 4
[B] => 1
[E] => 1
[I] => 3
[N] => 4
[R] => 2
[S] => 2
[T] => 2
[U] => 2
[V] => 1
[Y] => 1

案例#2:

[B] => 1
[I] => 1
[N] => 1
[S] => 1
[U] => 1
[0] => 2
[2] => 2

案例#3:

[A] => 1
[C] => 2
[D] => 1
[I] => 2
[N] => 1
[O] => 3
[R] => 2
[S] => 1
[U] => 1
[V] => 2
[1] => 1
[9] => 1

我尝试过的:

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

int main(){
    
    int tc, count=0, len;
    char str[500][500];
    
    scanf("%d", &tc); getchar();
    for(int i = 0; i < tc; i++){
        scanf("%[^\n]", str[i]); getchar();
        len = strlen(str[i]);
        printf("Case #%d:\n",i+1);
        for(int j = 0; j < len; j++){
            for(int k = 0; k < len; k++){
                if(str[i][j] >= 97 && str[i][j] <= 122) str[i][j]-=32;
                if(str[i][j] == 32) continue;
                if(str[i][j] == str[i][k]) count++;
                // remove duplicate char
                if(str[i][j] == str[i][k]){
                    
                }
            }
            printf("[%c] => %d\n", str[i][j], count);
            count=0;
        }
        printf("\n");
    }
        
    return 0;
}

【问题讨论】:

  • @anastaciu 我正在使用它,但我忘记删除它,谢谢顺便说一句
  • 这对我有帮助,谢谢@anastaciu
  • 您不需要那么多复杂性和时间。你应该可以做到O(n)en.m.wikipedia.org/wiki/Histogram

标签: arrays c


【解决方案1】:

当您需要计算集合中任何元素在给定集合中出现的次数时,最基本的方法之一是使用频率数组。很简单,一个频率数组包含了一个集合中可以出现的元素数量。 (例如,映射到每个索引的事物发生的频率是多少?)

例如,如果您想知道任何一个字母字符在一行文本中出现的次数(忽略大小写),您的集合有 26 个元素(字母表中的字母)。因此,您只需声明一个由 26 个元素组成的数组(通常是 intsize_t),初始化全部为零,然后循环遍历文本行中的字符,并为每个 alpha-字符,你增加数组的相应元素。

例如,对于数组int chars[26] = {0}; 和包含"Dog" 的行,您只需递增:

chars[toupper(line[0]) - 'A']++;   /* increments index chars[3]++  */
chars[toupper(line[1]) - 'A']++;   /* increments index chars[14]++ */
chars[toupper(line[2]) - 'A']++;   /* increments index chars[6]++  */

(请参阅ASCII Table and Description 以了解toupper(line[0]) - 'A' 如何将字符映射到相应的索引。另请注意转换为unsigned char 为清楚起见故意省略)

遍历完行中所有字符后,可以通过遍历频率数组的所有元素来输出结果,如果某个索引处的值大于零,则输出该索引对应的字符和该值作为计数持有,例如

for (int i = 0; i < 26; i++)
    if (chars[i])
        printf ("[%c] => %d\n", i + 'A', chars[i]);

要处理行中的数字字符,您只需使用第二个 10 元素频率数组并执行相同操作,但这次减去 '0' 以将 ASCII 数字映射到数组的元素 0-9

您忽略的问题的另一个方面是验证每个输入和转换。除非您检查返回以确定操作是成功还是失败,否则您无法正确使用任何输入函数。这同样适用于对数字类型的每次转换以及对代码的持续定义操作至关重要的任何其他表达式。

将这些部分放在一起,您可以使用全局 enum 为您的程序定义所需的常量(或使用多个 #define 语句定义同一事物)并打开您的文件并读取第一行并将其转换为整数价值:

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

enum { DIGITS=10, CHARS=26, MAXC=1024 };    /* constants for use in program */

int main (int argc, char **argv) {
    
    char line[MAXC];        /* buffer to hold each line of input */
    int ncases = 0;         /* integer for number of cases */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    
    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }
    
    if (!fgets (line, MAXC, fp)) {              /* read/validate 1st line */
        fputs ("(user canceled input)\n", stdout);
        return 0;
    }
    if (sscanf (line, "%d", &ncases) != 1) {    /* convert/validate to int */
        fputs ("error: invalid integer input for ncases.\n", stderr);
        return 1;
    }
    

现在有了ncases 中的案例数,您只需循环该次数,从文件中读取下一行,循环遍历line 中的每个字符,递增两个频率数组chars 的相应元素和digits 计算每种字符类型(alphadigits)的出现次数,然后依次输出在每个频率数组上循环的结果,以输出每个字符类型的计数。你可以这样做:

    for (int i = 0; i < ncases; i++) {      /* loop ncases times */
        int chars[CHARS] = {0},             /* frequency array for characters */
            digits[DIGITS] = {0};           /* frequency array for digits */
        
        if (!fgets(line, MAXC, fp)) {       /* read/validate line */
            fputs ("error: reading line from file.\n", stderr);
            return 1;
        }
        
        for (int j = 0; line[j]; j++) {     /* loop over each char in line */
            int c = toupper((unsigned char)line[j]);    /* convert to uppercase */
            if (isalpha((unsigned char)c))          /* check if A-Z */
                chars[c-'A']++;                     /* increment chars at index */
            else if (isdigit((unsigned char)c))     /* check if 0-9 */
                digits[c-'0']++;                    /* increment digits at index */
        }
        
        printf ("\nCase #%d:\n\n", i + 1);          /* output case no. */
        for (int j = 0; j < CHARS; j++)             /* loop over chars array */
            if (chars[j])                           /* if value at index non-zero */
                printf ("[%c] => %d\n", j + 'A', chars[j]); /* output count */
        for (int j = 0; j < DIGITS; j++)            /* loop over digits array */
            if (digits[j])                          /* if value at index non-zero */
                printf ("[%d] => %d\n", j, digits[j]);  /* output count */
    }

(注意: 用于输出位数,无需将索引映射回它们的 ASCII 值——您可以简单地使用 %d 输出整数表示,而不是映射 @987654344 @ 输出带有 %c 的 ASCII 字符 - 真的是你的选择。另请参阅 man 3 isalpha 以了解任何分类宏的每个参数必须为 unsigned char 类型的要求 - 解释上述转换的目的(unsigned char))

这基本上是解决频率阵列问题的完整代码。您所需要的只是整理并将成功返回给shell,例如

    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
    
    return 0;
}

使用/输出示例

将您的示例输入放在文件 dat/charcountlines.txt 中,然后将其作为输入提供给程序,将产生以下结果:

$ ./bin/charcount dat/charcountlines.txt

Case #1:

[A] => 4
[B] => 1
[E] => 1
[I] => 3
[N] => 4
[R] => 2
[S] => 2
[T] => 2
[U] => 2
[V] => 1
[Y] => 1

Case #2:

[B] => 1
[I] => 1
[N] => 1
[S] => 1
[U] => 1
[0] => 2
[2] => 2

Case #3:

[A] => 1
[C] => 2
[D] => 1
[I] => 2
[N] => 1
[O] => 3
[R] => 2
[S] => 1
[U] => 1
[V] => 2
[1] => 1
[9] => 1

以上匹配您指定的输出和排序。如果您还有其他问题,请仔细查看并告诉我。

【讨论】:

  • 什么是 MAXC
  • 这只是我用于line 可以容纳的最大字符数 的常数。 enum 中声明的每个常量都只是为在代码中使用而设置的,如果需要,在顶部进行设置可以方便地进行更改。用于声明字符数组char lines[MAXC];我通常会包含注释/* if you need a constant, #define one (or more) or use an enum */
  • 如果您有任何其他问题,请告诉我。我很乐意进一步解释。祝你编码好运!
  • 其实我还有3个,但我要先试试哈哈哈
猜你喜欢
  • 2015-06-07
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-12
  • 2021-07-02
相关资源
最近更新 更多