解决与最近元音程序的距离的问题

问题描述

我正在尝试编写一个函数,该函数接受一个字符串和每个字符,并返回到字符串中最近元音的距离。如果字符本身是元音,则返回 0,但是当我不知道我的代码有什么问题时,我的 s 向量(包含答案的所有值)中的前两个元素正在工作。其余的只是返回 0。我做错了什么?

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

std::vector<int> distance(std::string word){
    std::vector<char> r = {'a','e','i','o','u'};
    std::vector<int> s(word.length());
    for(int i = 0; i < word.length(); i++){
        if(std::find(r.begin(),r.end(),word[i]) == r.end()){
            for(int c = i + 1,d = i - 1; c < word.length() || d > 0; c++,d--){
                if(std::find(r.begin(),word[c]) != r.end()){
                    s[i] = c - i;
                    break;
                }
                else if(d >= 0 && std::find(r.begin(),word[d]) != r.end()){
                    s[i] = i - d;
                    break;
                }
            }
        }else s[i] = 0;
    }
    return s;
}

int main() {
  std::vector<int> h = distance("abbb");
    for(auto c : h){
        std::cout<<c<<"\n";
    }
}

解决方法

现代 C++ 中算法和有状态 Lambda 的威力。 . .

请先看 O(2n) 计算结果的代码:

#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <string>

// Function to calculate the distance to the nearest vowel
auto distanceToNextVowel(const std::string& s) {

    // Lambda to calculate if a character is a vowel
    auto isVowel = [](char c) { return (0x208222 >> (c & 0x1f)) & 1; };

    // Here we will store the resulting distance
    std::vector<size_t> dist(s.size());

    // Calculate distance from left and from right and use minimum
    std::transform(s.begin(),s.end(),dist.begin(),[&,i = s.size()](const char c) mutable { if (isVowel(c)) i = 0; else ++i;  return i; });
    std::transform(s.rbegin(),s.rend(),dist.rbegin(),i = s.size()](const char c,const size_t s) mutable { if (isVowel(c)) i = 0; else ++i;  return std::min(i,s); });

    // Return result to caller
    return dist;
}

// Test Driver
int main() {
    // This is our test string
    const std::string test{"Hello World"};

    // Caclcuate Result
    const auto dist = distanceToNextVowel(test);

    // Show result on console
    std::copy(test.begin(),test.end(),std::ostream_iterator<char>(std::cout,"\t")); std::cout << '\n';
    std::copy(dist.begin(),dist.end(),std::ostream_iterator<size_t>(std::cout,"\t")); std::cout << '\n';
    return 0;
}

呃!?那是什么?

这显示了在 C++ 中可以解决多么美妙的问题。但是,它需要很多解释。首先,如何检查一个字符是否是元音。

如果我们使用ASCII码来编码字母,那么我们会看到如下:

Ascci Code for Letters

我们看到大写和小写字母的 ASCII 码只是低 5 位不同。所以,如果我们用 0x1F 屏蔽 ASCII 代码,那么 char c{'a'}; unsigned int x{c & 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 位置,那么我们就有了一个元音。这已经有几十年的历史了。

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

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

生成的 Lambda 很简单:

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

酷。 . .


接下来,如何计算到/到下一个元音的距离。

让我们开始思考。如果我们从左到右遍历字符串,然后我们将结果索引位置设置为 0,如果我们找到一个元音。然后,我们简单地计算每个辅音,直到我们击中下一个元音。这将适用于从左到右计数。如果字符串不是以元音开头,那么我们使用一些大的指示符数字,例如 999,因为在这个方向上没有距离。示例:

H   e   l   l   o       W   o   r   l   d
999 0   1   2   0   1   2   0   1   2   3

接下来,如果我们从右到左使用完全相同的算法,那么我们会得到:

H   e   l   l   o       W   o   r   l   d
1   0   2   1   0   2   1   0   999 999 999

从左到右的最小距离是从左到右的相应值中的最小值。所以

String:         H   e   l   l   o       W   o   r   l   d
Left:           999 0   1   2   0   1   2   0   1   2   3
Right:          1   0   2   1   0   2   1   0   999 999 999
Min of above:   1   0   1   1   0   1   1   0   1   2   3

最后一行是结果。

现在,我们将使用有状态的 Lambda 来计算第一行:

std::transform(s.begin(),i = s.size()](const char c) mutable { if (isVowel(c)) i = 0; else ++i;  return i; });

因此,我们将遍历字符串。从左到右。如果找到元音,我们将距离设置为 0。如果找到辅音,我们将增加距离。距离值是 Lambda 的一个状态,我们用一个很大的值初始化它,这里是字符串的大小。

接下来,我们从右到左做同样的事情。我们将使用 std::transform 的第二种形式,在这里我们可以使用 2 个源容器并创建一个新目标。因此,我们将使用字符串和已经(从左到右)计算的距离向量并将结果再次存储在距离向量中,因为我们不需要新的。代码非常相似:

    std::transform(s.rbegin(),s); });

区别在于我们从右到左迭代,并存储我们刚刚计算的距离和之前已经计算的距离的最小值。

就是这样。

在 main 中,我们添加了一些驱动程序代码并在控制台上生成了一些输出。

我希望我能以易于理解的方式解释算法。

如有问题,请提问。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...