Obtain palindromic array by performing these operations
Asked Answered
M

3

6

An array is called palindromic if it remains the same after reversing the order of its elements.

You have an array of strings arr. For each i, arr[i] consists of at least two characters. For each pair of consecutive elements arr[i] and arr[i + 1], you can:

  1. Move the rightmost character of arr[i] to the leftmost position in arr[i + 1]. For instance, "abc" and "def" will become "ab" and "cdef". This operation can be applied only once to any pair of consecutive elements.
  2. Move the leftmost character of arr[i + 1] to the rightmost position in arr[i]. For instance, "abc" and "def" will become "abcd" and "ef". Again, this operation can be applied only once to any pair of consecutive elements.
  3. Do nothing to the pair of consecutive elements.

Is it possible to obtain a palindromic array from arr by performing these operations?

Consider the case when arr = {"aa", "bab", "cde", "aba", "ab"}. Here the output should be true and here is why:

Move first char from 1st index to the last position of 0th index, arr = {"aab", "ab", "cde", "aba", "ab"} Move last char from 3rd index to the first position of 4th index, arr = {"aab", "ab", "cde", "ab", "aab"}, which is a palindromic array.

I tried solutions such as two pointer, etc. but doesn't seem to work.

Miasma answered 24/4, 2023 at 21:19 Comment(5)
please include your code with the questionKatti
@Katti I tried two pointers approach but was struggling with test cases as: {"aa", "ba", "ab", "aa"}, etc. Overall I was not able to come up with a satisfactory solution for this problem. Is two-pointers the correct way to go for this problem? In my approach, I had two pointers, one starting from the 0th index and the other from the end of the array. If they were equal I moved to i++ and j-- otherwise, I did some manipulations. I don't have my code with me as it was asked in the coding test. Your input is hugely appreciated.Miasma
Can you repeat the operation on the same pair? For example {"aaaaaa","aa"} can be made palindromic but only if you do the 1st operation twice.Swirly
Instead of describing your attempt, edit your question and add the code of your attempt.Barrel
@RafałDowgird No for each pair we can apply the operation only once.Miasma
G
2

Your 2 pointer approach is fine, but needs some more observations.

Let's say your test case: {"aa", "ba", "ab", "aa"}. Initially, i=0 and j=3. As arr[0] = arr[3], you skip to i=1 and j=2. But you need to consider the following cases:

{"a", "aba", "ab", "aa"} Move the last char of arr[0]

{"aa", "ba", "aba", "a"} Move the first char of arr[3]

{"a", "aba", "aba", "a"} Move both, which is the palindromic case

{"aab", "a", "ab", "aa"} Move the first char of arr[1]

{"aa", "ba", "a", "baa"} Move the last char of arr[2]

{"aab", "a", "a", "baa"} Move both

Hope this help

Grano answered 27/4, 2023 at 4:14 Comment(0)
C
1

Recursive two pointers is potentially correct, but not an efficient solution. Consider an input that looks like:

{"aa", "aa", ..., "aa", "bb", "cc", "aa", "aa", ..., "aa"}

This cannot be turned into a palindrome, but two pointers will recursively try an exponential number of ways of moving a's around between strings before figuring that out.

However work your way from the outside in. For each pair of positions in the array, we can arrive in at most 9 states, and generally less. (Each side could have lost a letter to the outside, or have gained a letter from the outside, or be changed. So 3*3 = 9 states.) From each of those states we can likewise try 9 combinations of things. Any which makes this pair into a palindrome is a possible state for the next pair.

If ever we have 0 states, no palindrome is possible. If we meet in the middle, it is. For each pair we have a maximum of 81 operations (from each of 9 states, try 9 things) to discover which states are possible for the next one. Therefore this is not only correct, it also takes linear time in the length of the array to find the answer.

Covariance answered 25/4, 2023 at 18:11 Comment(0)
I
1

Today I worked the problem out. As stated by @btilly, there are 9 cases to consider in every step. Things to consider:

  • It is necessary to check every possible change, as moving one string might look like a solution (greedy approach) but it might be a move that would imply the other words won't work for the palindrome.
  • It is possible it is needed to make one move in every pair, meaning that no solution can be discarded beforehand.

The way to allow to diminish the amount of branches done doing a recursive approach is by being able to discard beforehand as much as possible, if we start with a pointer in the first element and one in the last element it is possible to just cut out from the recursion every branch that won't work as soon as possible. In every step we will compare the left word being evaluated and the right word, if they are equal then we proceed with the inner words, if not, we need to check every case for the left word (word without changes, word taking a char from the next word, word giving the last char to the next word) against every possible case for the last word being evaluated (same cases as before, word without changes, word loosing its first char and word borrowing the last char from the second to last word), and for every case that does make it palindrome we should recursively try with the rest of the words moving to the middle (left++ and right--). If we arrive to the case in which there is no words left or just one word left, then we arrived to a palindrome.

The code is quite extensive to write, here is my solution:

public  class WordsPalindromes {
    public static boolean solution(String[] arr) {
        return makePalindrome(Arrays.asList(arr));
    }

    public static boolean makePalindrome(List<String> words){
        if(words.size()==0 || words.size()==1)
            return true;
        if(words.size()==2){
            return words.get(0).equals(words.get(1)) ||
                    words.get(0).substring(0,words.get(0).length()-1).equals(words.get(0).substring(words.get(0).length()-1)+words.get(1)) ||
                    (words.get(0)+words.get(1).substring(0,1)).equals(words.get(1).substring(1));

        }
        boolean result = false;
        String first = words.get(0);
        String last = words.get(words.size()-1);
        String second = words.get(1);
        String secondLast = words.get(words.size()-2);
        int wSize = words.size();

        if(first.equals(last)){
            List<String> filtered =copy(words, 1, wSize-1);
            result = result||makePalindrome(filtered);
        }

        String firstShort = first.substring(0,first.length()-1);
        String secondLong = first.charAt(first.length()-1) + second;
        String firstLong = first + second.charAt(second.length()-1);
        String secondShort = second.substring(1);
        String lastShort = last.substring(1);
        String secondLastLong = secondLast + last.charAt(0);
        String lastLong = secondLast.substring(secondLast.length()-1) + last;
        String secondLastShort = secondLast.substring(0,secondLast.length()-1);

        if(firstShort.equals(last)){
            List<String>filtered = copy(words, 1,wSize-1);
            filtered.add(0,secondLong);
            result = result||makePalindrome(filtered);
        }

        if(firstShort.equals(lastShort)){
            List<String>filtered = copy(words, 2,wSize-2);
            filtered.add(0,secondLong);
            filtered.add(secondLastLong);
            result = result||makePalindrome(filtered);
        }

        if(firstShort.equals(lastLong)){
            List<String>filtered = copy(words, 2,wSize-2);
            filtered.add(0,secondLong);
            filtered.add(secondLastShort);
            result = result||makePalindrome(filtered);
        }

        if(first.equals(lastShort)){
            List<String>filtered = copy(words, 1,wSize-2);
            filtered.add(secondLastLong);
            result = result||makePalindrome(filtered);
        }

        if(first.equals(lastLong)){
            List<String>filtered = copy(words, 1,wSize-2);
            filtered.add(secondLastShort);
            result = result||makePalindrome(filtered);
        }

        if(firstLong.equals(last)){
            List<String>filtered = copy(words, 2,wSize-1);
            filtered.add(0,secondShort);
            result = result||makePalindrome(filtered);
        }

        if(firstLong.equals(lastShort)){
            List<String>filtered = copy(words, 2,wSize-2);
            filtered.add(0,secondShort);
            filtered.add(secondLastLong);
            result = result||makePalindrome(filtered);
        }

        if(firstLong.equals(lastLong)){
            List<String>filtered = copy(words, 2,wSize-2);
            filtered.add(0,secondShort);
            filtered.add(secondLastShort);
            result = result||makePalindrome(filtered);
        }

        return result;
    }

    public static List<String> copy(List<String> elements, int start, int end){
        List<String> result = new ArrayList<String>();
        for(int i=start;i<end;i++ ){
            result.add(elements.get(i));
        }
        return result;
    }
}

This solution performance can be improved by avoiding copying the list in every step, and use indexes instead.

Ibeam answered 6/5, 2023 at 15:45 Comment(2)
That looks too difficult for a coding assessment round! Thanks for sharing the solution!Miasma
Your code looks good, but you missed one constraint that every operation can only be applied once. You should add two counters.Am I right?Demagnetize

© 2022 - 2024 — McMap. All rights reserved.