【问题标题】:Removing Constants from a string从字符串中删除常量
【发布时间】:2021-05-04 06:13:37
【问题描述】:

我编写了一个代码来从字符串中删除常量并返回元音。我的代码不适用于包含以空格分隔的单词数的字符串。这是我的代码:

    int T;// integer T denoting the number of test cases. For each test case, we input a string.
cin>>T;
string s;
char arr[10] = {'a', 'e', 'i', 'o', 'u','A', 'E', 'I', 'O', 'U'};
for(int n = 0; n < T; n++){
    cin>>s;
    for(int i = 0; i < s.size(); i++){
        for(int j = 0; j < 10; j++){ 
            if(s[i] == arr[j]){cout<<s[i];}
        }
        if(isspace(s[i])){cout<<"n";}
    }
    cout<<endl;
}

输入: wdTSFuI IvfHOSNv , 它的正确输出是: uI IO , 我的输出是:uI

【问题讨论】:

  • 常量还是辅音?无论如何,作为新用户,请收下tour,阅读How to Ask。另外,请提取并提供minimal reproducible example。此外,请详细说明您的实际问题,因为您还没有正式提出问题。
  • 您的问题不是算法,而是您输入的类型:cin&gt;&gt;s; 它只将内容存储到s 中的第一个空格。 (operator>>(std::istream&, std::string&))。你可以考虑std::getline()。但要小心:混合o&gt;&gt;(用于T 的输入)和std::getline() 是另一回事。

标签: c++ string for-loop char output


【解决方案1】:

上述错误是因为当通过命令行输入输入时,C++ 省略了空格之后的数据,所以它只读取空格之前的数据,这就是为什么你得到的元音只出现在前半部分。

克服此错误的三种方法。

  1. 使用 getline 读取带有空格的字符串,新更改的代码是
#include <iostream>
#include <string>
using namespace std;

int main() {
    int T;// integer T denoting the number of test cases. For each test case, we input a string.
    cin>>T;
    string s;
    char arr[10] = {'a', 'e', 'i', 'o', 'u','A', 'E', 'I', 'O', 'U'};
    for(int n = 0; n < T; n++){
        getline(cin,s);     
        for(int i = 0; i < s.size(); i++){
            for(int j = 0; j < 10; j++){ 
                if(s[i] == arr[j]){
                    cout<<s[i];
                }
            }
            if(isspace(s[i])){cout<<"n";}
        }
        cout<<endl;
    }
}
  1. 使用字符数组来克服这个错误。

  2. 或者提供不带空格的输入。

我认为这可能会解决您的问题

【讨论】:

  • 感谢 Aravind,我也尝试了这段代码,但它对多个测试用例没有响应。
【解决方案2】:

当然解决方案是使用std::getline 读取整行,包括空格。

我不明白你的行if(isspace(s[i])){cout&lt;&lt;"n";},因为在你显示的输出示例中,你想显示空格而不是“n”。我想这是一个错字,应该是“”。

无论如何。使用std::getline 你的函数看起来像

#include <iostream>
#include <string>

int main() {

    // We need to operate on a given number of test cases
    size_t numberOfTestCases{}; std::cin >> numberOfTestCases;

    // Handle all test cases
    while (numberOfTestCases--) {

        // Read a complete line with text
        std::string line{}; std::getline(std::cin, line);

        // In case of space or vowel, print that
        for (const char c : line)
            if ((c == 32) or ((0x208222 >> (c & 0x1f)) & 1)) std::cout << c;
        std::cout << '\n';
    }
    return 0;
}

因为我看到了一些带有“测试用例”的东西,我猜你是在一些不神圣的“竞赛编程”网站上。

这就是为什么我使用条件((0x208222 &gt;&gt; (c &amp; 0x1f)) &amp; 1) 来检查一个字符是否是元音。这已经有几十年的历史了。对于这样的网站来说已经足够了。

如果有人对它的工作原理感兴趣,请发表评论,我将编辑答案并解释它。


编辑

根据要求,我将解释 for 循环。首先,我们有一个如此基于范围的 for 循环。和写法一样:

for (int i=0; i < line.length(); ++i) {
    const char c = line[i];
    . . .
}

然后你可以看到,我们首先检查字符 c 是否等于 32。32 是空格的 ASCII 码。你可以看到这个here


那么,现在,如何检查一个字符是否是元音。

如果我们使用 ASCII 码对字母进行编码,那么我们将看到以下内容:

我们看到大写和小写字母的 ASCII 码只是低 5 位不同。

所以,如果我们用 0x1F 屏蔽 ASCII 码,所以char c{'a'}; unsigned int x{c &amp; 0x1F},我们将得到 1 到 26 之间的值。

所以,我们可以为每个字母计算一个 5 位的值。

如果我们现在用 1 标记所有元音,我们可以构建一个由 32 位(无符号整数)组成的二进制数,并在元音为真的每个位置设置一个位。然后我们得到类似的东西

Bit position
3322 2222 2222 1111 1111 1100 0000 0000  
1098 7654 3210 9876 5432 1098 7654 3210  
Position with vowels:
0000 0000 0010 0000 1000 0010 0010 0010

这个数字可以转换为0x208222。

如果我们现在想知道,如果一个字母(无论是大写还是小写)是元音,那么我们从字符中屏蔽掉不必要的位( C & 1F )并将二进制数转移到与生成的字母代码一样多的位置。如果然后该位设置在 LSB 位置,则我们有一个元音。这已经有几十年的历史了。

示例:字符“e”。 ASCII 码是 101。用 1F 屏蔽会变成 5。然后我们将数字 0x208222(等于0000 0000 0010 0000 1000 0010 0010 0010)向右移动这 5 个位置,得到0000 0000 0000 0001 0000 0100 0001 0001。并且设置了带有“e”标记的最低位。因此,它是一个元音。

啊哈。不是那么容易,但适用于 ASCII 编码的字母。

顺便说一句,它也适用于其他字符选择。

结果是:

auto isVowel = [](char c) { return (0x208222 >> (c & 0x1f)) & 1; };

酷。 . .

【讨论】:

  • 谢谢 Armin,请您解释一下代码中的 FOR LOOP 部分,以便我理解。
  • @Tanya:我添加了解释。对不起我迟到了 。 . .
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
相关资源
最近更新 更多