0

Program should take input word from user and print out all the words in file with only one letter difference. For example: if input is: "way" output should be [say, may, day, war, waw, wax, was...] My code is finding words only with exact word which inputed. Like if input is "start", output is [restart, startup, started...] How can I change it to get output like above example? Here is a code:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

void fileRead(vector<string>& v, ifstream& fin) {
    string line;
    while (getline(fin, line)) {
        v.push_back(line);
    }
}
void search(vector<string>& v, string word) {
    for (int i = 0; i < v.size(); i++) {
        int index = v[i].find(word);
        if (index != -1)
            cout << v[i] << endl;
    }
}
int main() {
    vector<string> wordVector;
    ifstream fin("text.txt");
    if (!fin) {
        cout << "text.txt can't opened" << endl;
        return 0;
    }
    fileRead(wordVector, fin);
    fin.close();
    cout << "reading text.txt." << endl;
    while (true) {
        cout << "Input a word: >>";
        string word;
        getline(cin, word);
        if (word == "exit")
            break;
        search(wordVector, word);
    }
    cout << "Terminated" << endl;
}
A M
  • 14,694
  • 5
  • 19
  • 44
  • `int index = v[i].find(word);` looks for a substring, thus leading to your current results. Per your description you're looking for at least one common character (the *position* of which you never specified as being relevant, but your solitary example seems to suggest it is). Obviously that's the place to change things. You need to *not* look for substrings and rather look for shared *character(s)* (and possibly positions-thereof). – WhozCraig Jun 15 '22 at 03:06
  • I think so but I don't know exactly what should I use instead to find words only with one character difference – 서비로브자보키르 Jun 15 '22 at 03:24
  • Assuming the lengths must be identical or its automatic fail (and you should check that *first*), walk both words using the same index and keeping a flag initially set to false. when you come across a letter differential, check the flag, if its false, set it to true, if it was already true, then you have at least two letters different and its fail. If you finish and make it to the end of both words without triggering the fail case, then you have a winner. If you make it to the end and the flag is still false, then you have identical words (whether you want to report that is up to you). – WhozCraig Jun 15 '22 at 03:37
  • [Damerau-Levenshtein Edit Distance](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance) – kiner_shah Jun 15 '22 at 07:33

2 Answers2

0

I wrote this function find_all_alternate to find all possible alternate of a given word.

I've taken the list of words from here

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

// type alias
using word_list_t = std::vector<std::string>;
using word_t = std::string;

// find all alternative occurrence of a word in the given list
void find_all_alternate(word_list_t &list, word_t word)
{
    word_list_t search_list;

    // Filter out bigger words
    std::copy_if(list.begin(), list.end(),
                std::back_inserter(search_list),
                [=](auto s){
                    return s.size() == word.size();
    });

    auto len = word.size();
    std::set<std::string> results;

    while(len--) {
        std::string alt = word.substr(1, word.size()-1);
        for(const auto& w : search_list) {
            auto found = w.find(alt);
            if(found != std::string::npos) {
                results.insert(w);
            }
        }
        std::rotate(word.begin(), word.begin()+1, word.end());
    }

    std::cout << "All possible alternatives of word '" << word << "' are: " << std::endl;
    for(const auto &s : results) std::cout << s << std::endl;
    std::cout << std::endl;
}

int main(int argc, char *argv[])
{
    if(argc != 2) {
        std::cerr << "ERROR: Run the program as follows\n";
        std::cerr << "./word_search <word>\n";
        return -1;
    }

    std::string word_to_search = argv[1];
    std::ifstream in_file("words.txt", std::ios::in);
    if(!in_file.is_open()) {
        std::cerr << "Unable to open words.txt file\n";
        return -1;
    }

    std::string word;
    word_list_t word_list;

    while(in_file >> word) {
        word_list.push_back(word);
    }
    in_file.close();

    find_all_alternate(word_list, word_to_search);
}

Compilation and running

g++ -Wall -Wextra -Wpedantic -std=c++17 word_search.cpp -o word_search
./word_search  say

Output

All possible alternatives of word 'say' are:
aye
bay
day
gay
hay
isa
jay
kay
lay
may
pay
ray
sad
sam
san
sao
sap
sas
sat
saw
say
sys
Aamir
  • 1,974
  • 1
  • 14
  • 18
0

Your program shows this result, because the std::string find function will only look for an exact match of a substring.

What you want is to calculate the distance of 2 strings and then show those with a distance of 1.

For calculating the distance, generally the well known Levensthein algorithm is used.

I showed an implementation already in my answers here and here.

With that, your solution can be modified to

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <numeric>

using namespace std;


// Distance between 2 strings
size_t levensthein(const std::string& string1, const std::string& string2)
{
    // First get the string lengths
    const size_t lengthString1{ string1.size() };
    const size_t lengthString2{ string2.size() };

    // If one of the string length is 0, then return the length of the other
    // This results in 0, if both lengths are 0
    if (lengthString1 == 0) return lengthString2;
    if (lengthString2 == 0) return lengthString1;

    // Initialize substitition cost vector
    std::vector<size_t> substitutionCost(lengthString2 + 1);
    std::iota(substitutionCost.begin(), substitutionCost.end(), 0);

    // Calculate substitution cost
    for (size_t indexString1{}; indexString1 < lengthString1; ++indexString1) {
        substitutionCost[0] = indexString1 + 1;
        size_t corner{ indexString1 };

        for (size_t indexString2{}; indexString2 < lengthString2; ++indexString2) {
            size_t upper{ substitutionCost[indexString2 + 1] };
            if (string1[indexString1] == string2[indexString2]) {
                substitutionCost[indexString2 + 1] = corner;
            }
            else {
                const size_t temp = std::min(upper, corner);
                substitutionCost[indexString2 + 1] = std::min(substitutionCost[indexString2], temp) + 1;
            }
            corner = upper;
        }
    }
    return substitutionCost[lengthString2];
}


void fileRead(vector<string>& v, ifstream& fin) {
    string line;
    while (getline(fin, line)) {
        v.push_back(line);
    }
}
void search(vector<string>& v, string word) {
    for (int i = 0; i < v.size(); i++) {
        int distance = levensthein(word,v[i]);
        if (distance == 1)
            cout << v[i] << endl;
    }
}
int main() {
    vector<string> wordVector;
    ifstream fin("text.txt");
    if (!fin) {
        cout << "text.txt can't opened" << endl;
        return 0;
    }
    fileRead(wordVector, fin);
    fin.close();
    cout << "reading text.txt." << endl;
    while (true) {
        cout << "Input a word: >>";
        string word;
        getline(cin, word);
        if (word == "exit")
            break;
        search(wordVector, word);
    }
    cout << "Terminated" << endl;
}

And if you want to convert this to more modern C++, you can also use:

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

// Distance between 2 strings
size_t levensthein(const std::string& string1, const std::string& string2)
{
    // First get the string lengths
    const size_t lengthString1{ string1.size() };
    const size_t lengthString2{ string2.size() };

    // If one of the string length is 0, then return the length of the other
    // This results in 0, if both lengths are 0
    if (lengthString1 == 0) return lengthString2;
    if (lengthString2 == 0) return lengthString1;

    // Initialize substitition cost vector
    std::vector<size_t> substitutionCost(lengthString2 + 1);
    std::iota(substitutionCost.begin(), substitutionCost.end(), 0);

    // Calculate substitution cost
    for (size_t indexString1{}; indexString1 < lengthString1; ++indexString1) {
        substitutionCost[0] = indexString1 + 1;
        size_t corner{ indexString1 };

        for (size_t indexString2{}; indexString2 < lengthString2; ++indexString2) {
            size_t upper{ substitutionCost[indexString2 + 1] };
            if (string1[indexString1] == string2[indexString2]) {
                substitutionCost[indexString2 + 1] = corner;
            }
            else {
                const size_t temp = std::min(upper, corner);
                substitutionCost[indexString2 + 1] = std::min(substitutionCost[indexString2], temp) + 1;
            }
            corner = upper;
        }
    }
    return substitutionCost[lengthString2];
}


int main() {
    // Open file and check, if it could be opened
    if (std::ifstream fin("text.txt"); fin) {

        // Read all words from the file
        std::vector words(std::istream_iterator<std::string>(fin), {});

        // Read words to search for, until user enters exit
        std::string word{};
        while (word != "exit") {

            // Tell user, what to do
            std::cout << "\n\nInput a word: >> ";

            // Show result
            if (std::getline(std::cin, word) and word != "exit")
                std::copy_if(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"), [&](const std::string& s) { return 1u == levensthein(s, word); });
        }
    }
    else std::cerr << "\nError. Could not open source file\n\n";
}

And if there is the restriction that only words of equal length shall be shown, then you can write a simple comparison function by yourself.

This would look like:

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

bool oneLetterDifferent(const std::string& string1, const std::string& string2) {
    bool equalLength{ string1.length() == string2.length() };

    unsigned int unequalCounter{};
    if (equalLength) for (size_t i{}; i < string1.length(); ++i) {
        if (string1[i] != string2[i])
            ++unequalCounter;
    }
    return equalLength and (unequalCounter < 2);
}

int main() {
    // Open file and check, if it could be opened
    if (std::ifstream fin("text.txt"); fin) {

        // Read all words from the file
        std::vector words(std::istream_iterator<std::string>(fin), {});

        // Read words to search for, until user enters exit
        std::string word{};
        while (word != "exit") {

            // Tell user, what to do
            std::cout << "\n\nInput a word: >> ";

            // Show result
            if (std::getline(std::cin, word) and word != "exit")
                std::copy_if(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"), [&](const std::string& s) { return oneLetterDifferent(s, word); });
        }
    }
    else std::cerr << "\nError. Could not open source file\n\n";
}
A M
  • 14,694
  • 5
  • 19
  • 44