Проблема с решением расстояния до ближайшей гласной программы

#c

#c

Вопрос:

Я пытаюсь написать функцию, которая принимает строку и для каждого символа и возвращает расстояние до ближайшей гласной в строке. Если символ сам по себе является гласной, верните 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(), r.end(), word[c]) != r.end()){
                    s[i] = c - i;
                    break;
                }
                else if(d >= 0 amp;amp; std::find(r.begin(), r.end(), 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";
    }
}
 

Комментарии:

1. c < word.length() || d > 0 должно быть c < word.length() || d >= 0 .

2. Я бы использовал либо отладчик, либо некоторые операторы cout, чтобы посмотреть, что он делает. Это должно быть довольно очевидно, поскольку это не так сложно.

3. Спасибо! Это помогло!

Ответ №1:

Мощь алгоритмов и лямбд с сохранением состояния в современном C . . .

Пожалуйста, сначала посмотрите на код, который вычисляет результат в 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::stringamp; s) {

    // Lambda to calculate if a character is a vowel
    auto isVowel = [](char c) { return (0x208222 >> (c amp; 0x1f)) amp; 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(), [amp;, i = s.size()](const char c) mutable { if (isVowel(c)) i = 0; else   i;  return i; });
    std::transform(s.rbegin(), s.rend(), dist.rbegin(), dist.rbegin(), [amp;, 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 для букв

Мы видим, что код ASCII для прописных и строчных букв отличается только младшими 5 битами. Итак, если мы замаскируем код ASCII с помощью 0x1F, char c{'a'}; unsigned int x{c amp; 0x1F} то получим значения от 1 до 26. Таким образом, мы можем вычислить 5-битное значение для каждой буквы. Если мы теперь пометим все гласные 1, мы можем построить двоичное число, состоящее из 32 бит (unsigned int ) и установить бит в каждой позиции, где гласная является истинной. Затем мы получаем что-то вроде

 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 amp; 1F ) и сдвигаем двоичное число вправо на столько позиций, сколько имеет результирующий буквенный код. Если затем бит установлен в позиции LSB, то у нас есть гласная. Этому ноу-хау несколько десятилетий.

Ага. Не так просто, но будет работать для букв в кодировке ASCII.

Кстати, это также сработало бы для других вариантов выбора символов.

результирующая лямбда проста:

 auto isVowel = [](char c) { return (0x208222 >> (c amp; 0x1f)) amp; 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
 

И последняя строка — это результат.

Теперь мы будем использовать лямбда-выражение с полным состоянием для вычисления первой строки:

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

Итак, мы будем перебирать строку. Слева направо. Если мы находим гласную, мы устанавливаем расстояние равным 0. Если мы нашли согласную, мы увеличим расстояние. Значение расстояния — это состояние лямбда-выражения, которое мы инициализируем большим значением, здесь размером строки.

Затем, далее, мы делаем то же самое справа налево. Мы будем использовать вторую форму std::transform , где мы можем работать с 2 исходными контейнерами и создавать новую цель. Итак, мы будем использовать строку и уже (слева направо) вычисленный вектор расстояния и снова сохраним результат в distance-vector, потому что нам не нужен новый. Код очень похож:

     std::transform(s.rbegin(), s.rend(), dist.rbegin(), dist.rbegin(), [amp;, i = s.size()](const char c, const size_t s) mutable { if (isVowel(c)) i = 0; else   i;  return std::min(i, s); });
 

Разница в том, что мы выполняем итерацию справа налево и сохраняем минимальное значение нашего только что вычисленного расстояния и уже ранее вычисленного расстояния.

Вот и все.

В main мы добавляем некоторый код драйвера и генерируем некоторый вывод на консоль.

Надеюсь, я мог бы объяснить алгоритм понятным способом.

В случае возникновения вопросов, пожалуйста, задавайте.