【问题标题】:Finding pairs of non_vowels in C++在 C++ 中查找非元音对
【发布时间】:2019-03-13 13:00:01
【问题描述】:

我有一个编程问题,我应该编写一个程序来计算不是“a”、“e”、“i”、“o”的字母对(彼此相邻的字母)的数量, 'u'(元音)。

例子:

  • jas 0
  • olovo 0
  • skok 1 (sk)
  • stvarnost 4(st、tv、rn、st)

输入由组成单词不超过 200 个字符的小字母组成,输出应输出非元音字母对的数量(a、e、i、o、u)。

时间限制

  • 1 秒

内存限制

  • 64 MB

我遇到的问题中提供的示例:

  • 输入 skok
  • 输出 1

但是,当我输入“skok”字样时,程序无法运行(它似乎一直在后台运行,但屏幕上没有显示任何内容)。然而,“stvarnost”(现实)这个词有效,显示“4”——正如问题中给出的那样。

在 10 个测试用例中,两个测试用例给了我正确的输出,一个给我错误的输出,另外七个测试用例告诉我我已经超过了我的时间限制。

现在,我还想要一个关于如何避免超出给定时间限制的建议,以及如何在下面的程序中解决它。

这是我开始的代码:

#include <iostream>
#include <string.h>

using namespace std;

int main() {

    char zbor[200];
    cin.get(zbor, 200);
    int length = strlen(zbor);
    int j_value = 0;
    int check_pairs = 0;
    int pairs = 0;
    int non_vowels = 0;

    for (int i = 0; i < length; i++) {
        if (zbor[i] == 'a' || zbor[i] == 'e' || zbor[i] == 'i' || zbor[i] == 'o' || zbor[i] == 'u') {
            continue;
        } else {
            non_vowels++;
            for (int j = i + 1; j < length; j++) {
                if (zbor[j] == 'a' || zbor[j] == 'e' || zbor[j] == 'i' || zbor[j] == 'o' || zbor[j] == 'u') {
                    break;
                } else {
                    non_vowels++;
                    if (non_vowels % 2 != 0) {
                        check_pairs = non_vowels / 2 + 1;
                    } else {
                        check_pairs = non_vowels / 2;
                    }
                    if (pairs < check_pairs) {
                        pairs++;
                    }
                    j_value = j;
                }
            }
            i = j_value +  1;
        }
    }

    cout << pairs;

    return 0;
}

编辑:

#include <iostream>
#include <string.h>

using namespace std;

int main() {

    char zbor[200];
    cin.get(zbor, 200);
    int length = strlen(zbor);
    int pairs = 0;
    int non_vowels = 0;

    for (int i = 0; i < length; i++) {
        if (zbor[i] == 'a' || zbor[i] == 'e' || zbor[i] == 'i' || zbor[i] == 'o' || zbor[i] == 'u') {
            non_vowels = 0;
            continue;
        } else {
            non_vowels++;
            if (non_vowels >= 2) {
                if (non_vowels % 2 != 0) {
                    pairs = non_vowels / 2 + 1;
                } else if (non_vowels % 2 == 0) {
                    pairs = non_vowels / 2;
                }
            }
        }
    }

    cout << pairs;

    return 0;
}

使用以下答案的代码片段(brunoOzzy's)编辑了代码,这是有效的最终版本:

#include <iostream>
#include <string.h>

using namespace std;

bool vowel(char c) {
    switch(c) {
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
        return true;
    default:
        return false;
    }
}

int main()
{

    char zbor[200];
    cin.get(zbor, 200);
    int N = strlen(zbor);
    int non_vowels = 0;
    int pairs = 0;

    for (int i = 0; i < N; i++) {
        if (!vowel(zbor[i])) {
            non_vowels = 0;
        } else {
            non_vowels++;
            if (!vowel(zbor[i])) {
                non_vowels = 0;
            } else {
                non_vowels++;
                if (non_vowels > 1) {
                    pairs++;
                }
            }
        }
    }

    cout << pairs;

    return 0;
}

【问题讨论】:

  • 我没有看到任何问题,但它看起来像是调试器的工作。
  • 研究std::adjacent_find
  • 可以在评论区分享问题链接吗?
  • 听起来你可能需要学习如何使用调试器来单步调试你的代码。使用好的调试器,您可以逐行执行您的程序,并查看它与您期望的偏差在哪里。如果您要进行任何编程,这是必不可少的工具。进一步阅读:How to debug small programsDebugging Guide
  • 每次到达非元音时增加non_vowels,以检查奇偶校验不允许检查非元音是否连续。你的算法错了

标签: c++ arrays string char word


【解决方案1】:

您可以使用 C++ 功能轻松简化代码,建议:

#include <algorithm>
#include <iostream>
#include <array>
#include <vector>

const std::array<char, 5> vowels = {'a', 'e', 'i', 'o', 'u'};

bool isVowel(char c)
{
    return std::find(vowels.begin(), vowels.end(), c) != vowels.end();
}

bool checker(char c)
{
    static bool lastFound = false;

    if (lastFound && !isVowel(c))
        return true;
    else
        lastFound = !isVowel(c);

    return false;   
}

int main()
{
    std::vector<char> v{'s', 'k', 'o', 'k', 'a', 'k'};

    int num_items = std::count_if(v.begin(), v.end(), &checker);

    std::cout << num_items << std::endl;
}

【讨论】:

  • 如果你只使用std::adjacent_find(),就不需要有状态的checker()(你如何重置它以备下次使用?)。
【解决方案2】:

这是使用std::adjacent_find的解决方案:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>

bool isConsonant(char c)
{
    static const char *vowels = "aeiou";
    return strchr(vowels, c)?false:true;
}

int main()
{
    std::string s = "stvarnost";
    auto it = s.begin();
    int count = 0;
    while (true)
    {
        // find the adjacent consonants
        auto it2 = std::adjacent_find(it, s.end(), 
                                     [&](char a, char b) 
                                     { return isConsonant(a) && isConsonant(b); });
        if ( it2 != s.end())
        {
            // found adjacent consonents, so increment count and restart at the next character.
            ++count;
            it = std::next(it2);
        }
        else
            break;
    }
    std::cout << count << "\n";
}

输出:

4

Live Example

【讨论】:

    【解决方案3】:

    这是一个使用 string_view 和相邻查找的 C++17 解决方案。它是惯用的 C++,因此非常简短易懂。

    #include <algorithm>
    #include <iostream>
    #include <string_view>
    using namespace std;
    
    bool checkPair(char a, char b)
    {
        string_view vowels { "aeiou" };
        bool aNotVowel = vowels.find(a) == string_view::npos;
        bool bNotVowel = vowels.find(b) == string_view::npos;
        return aNotVowel && bNotVowel;
    }
    
    int main(int argc, char **argv)
    {
        int pairs { 0 };
        string_view input(argv[1]);
    
        for(auto next = input.begin(); next != input.end();)
        {
            next = adjacent_find(next, input.end(), checkPair);
            if(next != input.end())
            {
                ++pairs;
                ++next;
            }
        }
        cout << pairs << endl;
    
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      确实,有几件事..

      1. 访问 zbor[i] 可能非常耗时,因为每次比较时它总是再次查找位置 i(除非编译器优化了某些东西)

      2. 不需要第二个 for 循环,您可以只计算最后一个条目是否为元音,这就是您在进一步处理字符串时需要的所有信息。

      3. 利用 lambda 等 c++ 功能使您的代码更易于阅读

      .

      auto isVowel = [](char& c) {return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';};
      
      for (int i = 0; i < length; i++)
      {
        if (isVowel(zbor[i]))
        {
          non_vowels = 0;
        }
        else
        {
          non_vowels++;
      
          if (non_vowels > 1)
            pairs++;
        }
      }
      
      cout << pairs;
      

      【讨论】:

      • 为什么要检查同一个zbor [i]是否在non_vowels++之后两次是元音。
      • 哇,好点子。编辑了代码,因为非元音永远不是元音:S
      【解决方案5】:
      1. 优化时间:解析字符串(char *)时,通过使用“while”而不是获取长度并使用“for”。您只获得 1 次通过,而不是 2 次(strlen 已通过 1 次)。
      2. 优化代码:如果您有重复的代码,请将其放入函数或宏中。
      3. 使用之前的两点,这里是一个示例代码(你可以处理输入管理):

      样本

      #include <iostream>
      #include <cstddef>
      
      using namespace std;
      
      #define IS_VOWEL(a) ((a == 'a') || (a == 'i') || (a == 'u') || (a == 'e') || (a == 'o') || (a == 'y'))
      
      int find_pairs(char * input, char ** stop_pos)
      {
          char * input_cursor = input;
      
          while ((*input_cursor != 0) && IS_VOWEL(*input_cursor))
          {
              input_cursor++;
          }
          size_t used_count = 0;
          while ((*input_cursor != 0) && !IS_VOWEL(*input_cursor))
          {
              used_count++;
              input_cursor++;
          }
          *stop_pos = input_cursor;
          if (used_count < 2)
          {
              return 0;
          }
          else
          {
              return used_count - 1;
          }
      }
      
      int main()
      {
          char input[] = "stvarnost";
          char * input_cursor = input;
          int found = 0;
          while (*input_cursor != 0)
          {
              found += find_pairs(input_cursor, &input_cursor);
          }
          cout << found << endl;
          return 0;
      }
      

      玩得开心:)

      【讨论】:

      • 宏是邪恶的。比如IS_VOWEL(3.14159264)的结果是什么?函数有更好的类型检查。
      • 你应该更喜欢std::string,因为它比字符数组更安全。您可以使用operator[] 访问字符串中的字符。
      • @ThomasMatthews 我知道宏不好,但这只是一个需要增强的示例。对于字符串与字符数组,如果您正确检查输入,您可以使用它,算法背后的整个逻辑也是指针操作。
      【解决方案6】:

      我认为,对于要求,您可以使程序看起来更简单,如下所示,我对您提供的示例进行了测试和工作。我没有获取输入,而是通过硬编码输入进行测试,您可以更改:

      #include <iostream>
      #include <string.h>
      
      using namespace std;
      
      bool IsVowel(char ch)
      {
          switch (ch)
          {
          case 'a':
          case 'e':
          case 'i':
          case 'o':
          case 'u':
          case 'A':
          case 'E':
          case 'I':
          case 'O':
          case 'U':
              return true;
              //break;
          default:
              return false;
          }
      
          return false;
      }
      
      int CountNonVowelPair(char* chIn)
      {
              char zbor[200];
              //cin.get(zbor, 200);
              strcpy(zbor, chIn);
              int length = strlen(zbor);
              int j_value = 0;
              int check_pairs = 0;
              int pairs = 0;
              int non_vowels = 0;
      
              if (length <= 1)
                  return 0;
      
              for (int i = 1; i < length; i++)
              {
                  if (IsVowel(zbor[i - 1]) || IsVowel(zbor[i]))
                      continue;
                  else
                      pairs++;
              }
      
              return pairs;
      }
      
      int main() 
      {
          int nRet;
          nRet = CountNonVowelPair("jas");
          cout << nRet <<endl;
          nRet = CountNonVowelPair("olovo");
          cout << nRet <<endl;
          nRet = CountNonVowelPair("skok");
          cout << nRet <<endl;
          nRet = CountNonVowelPair("stvarnost");
          cout << nRet <<endl;
      
          return 0;
      }
      

      【讨论】:

      • 您可以使用std::toupperstd::tolower 来减小switch/case 语句的大小。另外,请考虑使用std::string 而不是字符数组,因为数组可能会溢出(或者更糟的是,它们可能没有终止的 nul 字符)。
      【解决方案7】:

      提案:

      #include <iostream>
      
      using namespace std;
      
      bool vowel(char c)
      {
          switch (c) {
          case 'a':
          case 'e':
          case 'i':
          case 'o':
          case 'u':
          case 'y':
              return true;
          default:
              return false;
          }
      }
      
      int main()
      {
          string zbor;
      
          if (! (cin >> zbor))
              return -1;
      
          int pairs = 0;
      
          for (size_t i = 0; i < zbor.length(); ++i) {
              if (!vowel(zbor[i])) {
                int n = 0;
      
                do
                  n += 1;
                while ((++i != zbor.length()) && !vowel(zbor[i]));
      
                pairs += n - 1;
              }
          }
      
          cout << pairs << endl;
      
          return 0;
      }
      

      编译和执行:

      /tmp % g++ -pedantic -Wextra p.cc
      /tmp % ./a.out
      jas
      0
      /tmp % ./a.out
      olovo
      0
      /tmp % ./a.out
      skok
      1
      /tmp % ./a.out
      stvarnost
      4
      

      【讨论】:

      • “size_t”部分是什么意思?
      • @N.T. size_t 是正确的类型,而不是 intunsigned int 在很多情况下,例如 string::length() 返回 a size_t
      • @N.T.在这里,size_tstd::size_t 相同 - 请注意靠近顶部的讨厌的 using namespace std;(并阅读 Why is “using namespace std” considered bad practice?)。
      • 我们在学校被教导使用 'using namespace std;'所以我习惯了,不过我会改掉这个习惯。
      • @N.T.在 15 年的 C++ 中,我从来没有因为using namespace std; 而遇到过问题;-)
      猜你喜欢
      • 2021-11-02
      • 1970-01-01
      • 1970-01-01
      • 2011-12-13
      • 2015-11-25
      • 2014-05-18
      • 2020-09-29
      • 2018-02-12
      • 1970-01-01
      相关资源
      最近更新 更多