Minimum clicks to convert string X to Y

Given a starting string X of 3 characters, finishing string Y of 3 characters and an array of forbidden strings. The task is to find the minimum number of clicks to reach Y from X.

Rules:

  • Each of the 3 characters changes in a circular manner i.e. on each click you can go from either a to b or a to z and none of the forbidden words is ever displayed.
  • If it is not possible to reach Y, print -1. In each click, only a single letter can be changed.
  • Each forbidden string is of the form: {“S1” “S2” “S3”} where each string Si contains forbidden letters for that character.
  • Forbidden string = {“ac” “bx” “lw”} implies words “abl”, “cxw”, “cbl”, “abw”, “cbw”, “axl”, “axw” and “cxl” are forbidden and will never be displayed.
  • Note: If the starting string X is also a possible combination of forbidden characters, then also the result should be -1.

    Input: X = “znw”, Y = “lof”, N = 4 (no of forbidden strings)
    forbidden =
    { ” qlb “, ” jcm “, ” mhoq ” },
    { ” azn “, ” piy “, ” vj ” },
    { ” by “, ” oy “, ” ubo ” },
    { ” jqm “, ” f “, ” ej ” }
    Output: Minimum no of clicks required is 22
    Explanation: Since no combination out of the given forbidden strings forms the final string Y, so the string Y becomes valid.

    Thus minimum number of clicks required is computed as:
    z – l in forward direction by 12 clicks
    z – l in backward direction by 14 clicks
    n – o in forward direction by 1 click
    n – o in backward direction by 25 clicks
    w – f in forward direction by 9 clicks
    w – f in backward direction by 17 clicks



    Total minimum clicks = 12 + 1 + 9 = 22.

    Input: X = “bdb”, Y = “xxx”, N = 1 (no of forbidden strings)
    forbidden = { ” ax “, ” acx “, ” bxy ” }
    Output: Minimum no of clicks required is -1
    Explanation: As “xxx” is a possible combination of forbidden characters, therefore it is not possible to reach Y from X.

    Approach:

    Use BFS(Breadth First Search) with certain modifications in order to get the min number of clicks, bypassing the forbidden strings.     

    1. Since each of the 3 positions can contain alphabets, hence create a 3D visited array of dimensions 26 * 26 * 26 in order to traverse the word states.
    2. To visualize each of the restricted words, create another 3D array of dimensions 26 * 26 * 26 to keep track of the words which must never be visited in the traversal.
    3. Since each of the 3 characters changes in a circular manner i.e, letters will change in a circular fashion on each click, there is a need to take care to modulo 26 every time the next letter is reached. 
    4. Let current state of word be [X Y Z]. Then on a single click movement to the following 6 states is possible :

       [ X+1 Y Z ], [ X-1 Y Z ], [ X Y+1 Z ], [ X Y-1 Z ], [ X Y Z+1 ], [ X Y Z-1 ].

    5. Hence create 3 utility arrays dx, dy, dz to keep the traversal a streamlined process. Store each word state in a struct having 4 fields namely a, b, c (each of the 3 characters) and distance from the starting words.

    Below is the implementation of above approach:

    C++

    filter_none

    edit
    close

    play_arrow

    link
    brightness_4
    code

    // C++ code for above program.
    #include <bits/stdc++.h>
    using namespace std;
    #define int long long int
      
    // each node represents a word state
    struct node {
        int a, b, c;
        // dist from starting word X
        int dist;
    };
      
    // 3D visited array
    bool visited[26][26][26];
      
    // 3D restricted array
    bool restricted[26][26][26];
      
    // utility arrays for single step
    // traversal in left and right
    int dx[6] = { 1, -1, 0, 0, 0, 0 };
    int dy[6] = { 0, 0, 1, -1, 0, 0 };
    int dz[6] = { 0, 0, 0, 0, 1, -1 };
      
    // function to find the
    // minimum clicks.
    void solve(string start,
               string end, int qx,
               const vector<vector<string> >& forbidden)
    {
      
        memset(visited, 0,
               sizeof(visited));
        memset(restricted, 0,
               sizeof(restricted));
      
        for (auto vec : forbidden) {
      
            string a = vec[0];
            string b = vec[1];
            string c = vec[2];
      
            for (auto x : a)
                for (auto y : b)
                    for (auto z : c) {
      
                        // each invalid word is
                        // decoded and marked as
                        // restricted = true.
                        restricted[x - 'a']
                                  [y - 'a']
                                  [z - 'a']
                            = true;
                    }
        }
      
        // starting and ending letter a
        int sa = start[0] - 'a';
        int ea = end[0] - 'a';
      
        // starting and ending letter b
        int sb = start[1] - 'a';
        int eb = end[1] - 'a';
      
        // starting and ending letter c
        int sc = start[2] - 'a';
        int ec = end[2] - 'a';
      
        if (restricted[sa][sb][sc]
            or restricted[ea][eb][ec]) {
      
            // check if starting word
            // or finishing word is
            // restricted or not
            cout << -1 << endl;
      
            return;
        }
      
        // queue of nodes for BFS
        queue<node> q;
      
        // initial starting word pushed in
        // queue. dist = 0 for starting word
        q.push({ sa, sb, sc, 0 });
      
        // mark as visited
        visited[sa][sb][sc] = true;
      
        while (!q.empty()) {
            node x = q.front();
            q.pop();
      
            // final destination reached condition
            if (x.a == (end[0] - 'a')
                and x.b == (end[1] - 'a')
                and x.c == (end[2] - 'a')) {
      
                cout << x.dist
                     << endl;
                return;
            }
      
            int DIST = x.dist;
            for (int i = 0; i < 6; i++) {
      
                // mod 26 for circular letter sequence
      
                // next letter for a
                int A = (x.a + dx[i] + 26) % 26;
      
                // next letter for b
                int B = (x.b + dy[i] + 26) % 26;
      
                // next letter for c
                int C = (x.c + dz[i] + 26) % 26;
      
                if (!restricted[A][B][C]
                    and !visited[A][B][C]) {
      
                    // if a valid word state,
                    // push into queue
                    q.push({ A, B, C, DIST + 1 });
                    visited[A][B][C] = true;
                }
            }
        }
      
        // reach here if not possible
        // to reach final word Y
        cout << -1 << endl;
    }
      
    // Driver Code
    signed main()
    {
        // starting string
        string X = "znw";
      
        // final string
        string Y = "lof";
      
        // no of restricting word vectors
        int N = 4;
      
        vector<vector<string> > forbidden
            = { { "qlb", "jcm", "mhoq" },
                { "azn", "piy", "vj" },
                { "by", "oy", "ubo" },
                { "jqm", "f", "ej" } };
      
        solve(X, Y, N, forbidden);
        return 0;
    }

    chevron_right

    
    

    Output:

    22
    


    Time Complexity: O(26 * 26 * 26), since at max, there can be 26*26*26 word states.
    Space Complexity: O(26 * 26 * 26)

    competitive-programming-img




    My Personal Notes arrow_drop_up

    Recommended Posts:


    Jadavpur University IT Undergrad 22

    If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

    Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.