0
#include <bits/stdc++.h>
using namespace std;
#include <unordered_set>
#include <queue>
struct word {
    string s;
    int level;
    word(string a, int b)
        : s(a)
        , level(b)
    {
    }
};
bool isadj(string s1, string s2)
{
    int len = s1.length(), count = 0;
    for (int i = 0; i < len; i++) {
        if (s1[i] != s2[i])
            count++;
        if (count > 1)
            return false;
    }
    return count == 1 ? true : false;
}
int ladderLength(string beginWord, string endWord, vector<string>& wordList)
{
    unordered_set<string> st;
    for (string s : wordList)
        st.insert(s); // adding elements into a set
    if (st.find(endWord) == st.end())
        return 0;
    queue<word> q;
    q.push(word(beginWord, 0)); // initialising the queue

    while (!q.empty()) {
        word temp = q.front(); // pop the current string
        q.pop();
        if (temp.s == endWord)
            return temp.level;
        for (auto it = st.begin(); it != st.end(); it++) { // loop over the set to find strings at a distance of 1 and add them to the queue
            if (isadj(temp.s, *it)) // i have inserted code here to print the string *it
            {
                q.push(word(*it, temp.level + 1));
                st.erase(*it); // delete the element to avoid looping
            }
        }
    }
    return 0;
}

int main()
{
    // make dictionary
    vector<string> D;
    D.push_back("poon");
    D.push_back("plee");
    D.push_back("same");
    D.push_back("poie");
    D.push_back("plie");
    D.push_back("poin");
    D.push_back("plea");
    string start = "toon";
    string target = "plea";
    cout << "Length of shortest chain is: "
         << ladderLength(start, target, D);
    return 0;
}

The problem i am trying to solve is https://leetcode.com/problems/word-ladder/ I am unable to trace where I am using a memory that was deallocated again in my program?

The following are my attempts to debug :

I tried to run it on another online ide where the code compiles and runs successfully but gives a wrong answer . in order to debug it I have inserted some lines into my code in order to print all the strings which are at a distance of 1 for my current string. surprisingly an empty string is appearing to be in the set. Please help me in understanding where am I doing a mistake.

venkat
  • 13
  • 6
  • 7
    You should configure an IDE locally and run it with a debugger attached. Using only online tools for development is a bad approach. – François Andrieux Aug 26 '20 at 16:39
  • 4
    You seem to use `include ` without knowing what it actually does. So please read [this](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) and also [Why is “using namespace std;” considered bad practice?](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – Lukas-T Aug 26 '20 at 16:41
  • maybe not the current problem, but in `isadj` you silently assume that `s2` is not longer than `s1`, if it is you are accessing `s2` out-of-bounds – 463035818_is_not_an_ai Aug 26 '20 at 16:42
  • @idclev463035818 both all the strings are assumed to be of the same length – venkat Aug 26 '20 at 16:45
  • definitely not the current problem: In many places you are making unnecessary copies. Passing parameters and `for (string s : wordList) st.insert(s);`, if I counted correctly the strings (eg `"poon"`) are copied 3 times before they end up in the unordered_set – 463035818_is_not_an_ai Aug 26 '20 at 16:46

3 Answers3

5

unordered_set::erase returns a value, and this returned value is important. You should not ignore it.

In your case, once you erase something from the set, it is invalid. Trying to increment it results in Undefined Behavior.

The correct approach is to replace the current iterator with the returned one, then not increment during the loop.

for (auto it = st.begin(); it != st.end(); )
    if (...) {
        // ...
        it = st.erase(*it);
    } else
        ++it;
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • 1
    Side note: In many cases you will find the [Erase-Remove Idiom](https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom) helpful . – user4581301 Aug 26 '20 at 17:10
  • in the solution suggested above it = st.erase(*it); still doesnt work . when i just use the iterator it = st.erase(it); the problem is solved . – venkat Aug 26 '20 at 18:53
  • @venkat Did you change to `for` loop to not increment the iterator? Your solution declares a new `it` variable. – 1201ProgramAlarm Aug 26 '20 at 19:21
1

After the line:

st.erase(*it); // delete the element to avoid looping

the it iterator is not valid and should not be used.

B0FEE664
  • 191
  • 5
  • Correctly diagnosed, but a good answer provides or suggests a solution. – user4581301 Aug 26 '20 at 17:11
  • @BOFEE664 can u please elaborate on why is it not valid after that line? – venkat Aug 26 '20 at 18:56
  • 1
    @venkat An Iterator refers to an item in the set. If you remove this item from the set, the iterator no longer refers to an item in the set. Since the iterator no longer refers to an item in the set, correctly iterating to the next item in the set cannot be guaranteed. Some good, general reading on the invalidation of iterators: [Iterator invalidation rules](https://stackoverflow.com/questions/6438086/iterator-invalidation-rules) – user4581301 Aug 26 '20 at 20:38
0

Your problem seems to be already addressed, but if you'd be interested, this'd also pass without using std::queue, only using std::unordered_set:

// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(NULL);
    std::cout.tie(NULL);
    return 0;
}();


// Most of headers are already included;
// Can be removed;
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_set>
#include <algorithm>

using ValueType = std::int_fast16_t;

static const struct Solution {
    static const int ladderLength(
        const std::string start,
        const std::string end,
        const std::vector<std::string>& words
    ) {
        std::unordered_set<std::string> words_map(std::begin(words), std::end(words));
        std::unordered_set<std::string> head;
        std::unordered_set<std::string> tail;
        std::unordered_set<std::string>* curr_head;
        std::unordered_set<std::string>* curr_tail;

        if (words_map.find(end) == std::end(words_map)) {
            return 0;
        }

        head.insert(start);
        tail.insert(end);
        ValueType ladder = 2;

        while (!head.empty() && !tail.empty()) {
            if (head.size() < tail.size()) {
                curr_head = &head;
                curr_tail = &tail;

            } else {
                curr_head = &tail;
                curr_tail = &head;
            }

            std::unordered_set<std::string> temp_word;

            for (auto iter = curr_head->begin(); iter != curr_head->end(); iter++) {
                std::string word = *iter;

                for (ValueType index_i = 0; index_i < word.size(); index_i++) {
                    const char character = word[index_i];

                    for (ValueType index_j = 0; index_j < 26; index_j++) {
                        word[index_i] = 97 + index_j;

                        if (curr_tail->find(word) != curr_tail->end()) {
                            return ladder;
                        }

                        if (words_map.find(word) != std::end(words_map)) {
                            temp_word.insert(word);
                            words_map.erase(word);
                        }
                    }

                    word[index_i] = character;
                }
            }

            ladder++;
            curr_head->swap(temp_word);
        }

        return 0;
    }
};

You might want to break it into more methods, a bit too long for a function.


References

  • For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.
Emma
  • 27,428
  • 11
  • 44
  • 69