Найти символы в одной позиции в массиве строк

Попался алгоритм, ничего даже приблизительно похожего на ответ выдавить из себя не смог, так что только задача.

Дан массив S, состоящий из N строк. Каждая строка имеет одинаковую длину M. Задача — найти пару строк в массиве S, в которых обе строки имеют одну и ту же букву в той же позиции.

Например:

S = ["abc", "bea", "dbe'], строка 0 "abc" и строка 2 "dbe" имеют одну и ту же букву "b" в позиции 1. С другой стороны, для строк «abc» и «bea» не существует позиции, в которой они имеют одну и ту же букву. Если такой пары нет, функция должна вернуть пустой массив. Если есть больше, чем один правильный ответ, функция может вернуть любой из них.

Результат - это массив, состоящий из трех целых чисел. Первые два целых числа — это индексы в S строки, принадлежащие паре. Третье целое число — это позиция общей буквы.

Еще примеры:

S = ["abc", "bea", "dbe"] результ: [0, 2, 1]. Другой правильный ответ: [2, 0, 1], так как порядок индексов строк значения не имеет.

S = ["abc", "bca", "dbe"] -> [0, 2, 1]

S = ["zzzz", "ferz", "zdsr", "fgtd'] может возвращать [0, 1, 3] так как "zzzz", и "ferz" имеют "z" в позиции 3. функция также может возвращать [1, 3, 0] что будет отражать строки "ferz", "fgtd" по букве "f".

S = ["gr", "sd", "rg"] должна возвращать [] так как нет пары строк, удовлетворяющих критериям

S = ["bdafg", 'ceagi'] функция должна вернуть [0, 1, 2]

Напишите эффективный алгоритм для следующих предположений:

  1. N это int в диапазоне [1..30000];
  2. M это int в диапазоне [1..2000];
  3. Каждый элемент S состоит только из строчных латинских букв (a-z);
  4. N * M < 30 000.

Ответы (2 шт):

Автор решения: had0uken

Решил так: (вышло довольно "топорно - на пролом", но все примеры из вашего вопроса проходит правильно). Я добавлял комментарии по ходу кода, но если какой-то пункт не понятен, то спрашивайте. Фактически я задачу разделил на 3 задачи: каждая задача - это вычисление одного из элементов массива на выходе. Начал с последнего элемента и закончил нулевым.

1)Поиск индекса который повторяется. Учитывая все стринги в массиве по условию задачи имеют одинаковую длину, я добавляю каждый 0 символ в LIST (проверяя нет ли такого же символа в листе). И если все добавились, перехожу на следующий символ и добавляю 1 символ со всех стрингов. И так пока, не обнаружу позицию в стрингах, в которых повторяется символ. И записываю ее в result[2]. теперь мы знаем в каком месте искать повторения.

  1. зная позицию в Стрингах, в которых нужно искать повторения - воспользуемся множеством SET. Мы с каждого стринга добавляем символ под уже найденной позицией в прошлом пункте. И если сет говорит о том, что такой символ уже есть в множестве - мы нашли Стринг, в котором повторяется символ. Записываем в result[1]

3)Самое легкое - вторую строку, и у которой такой же индекс как в стринге найденном во втором пункте на позиции, найденной в 0 пункте. Записываем ее в result[0]. Возвращаем result

public class Test {
    public static void main(String[] args) {
        String[] s = {"bdafg", "ceagi"};

        System.out.println("Final answer:"+ Arrays.toString(method(s)));
    }

    public static int[] method(String[] array){
        int n=array.length;
        int m=array[0].length();
        int [] result = new int[3]; // word1 word2 index
        for(int i=0;i< result.length;i++)
            result[i]=-1;

        //создаем двухмерный массив чаров на основе исходного массива:

        char [][] ch = new char[n][m];


        for(int i=0;i<n;i++)
            for (int j=0;j<m;j++)
                ch[i][j]=array[i].charAt(j);


        //print(ch);

        //определяем индекс который повторяется и записываем его в result на [2] позицию.
        List<Character> list = new ArrayList<>();
        for(int j=0;j<m;j++) {
            for (int i = 0; i < n; i++)
                if (!list.contains(ch[i][j])) list.add(ch[i][j]);
            if(list.size()<n){result[2]=j; break;}
            else list.clear();
        }

        if(result[2]==-1) {
            result=new int[0];
            return result;              // повторений не найдено, возвращаем пустой массив
        }


        //System.out.println(Arrays.toString(result));

        list.clear();
        //определяем слово которое повторяется и записываем его на [1] позицию

        for(int i=0;i< array.length;i++)
            list.add(array[i].charAt(result[2]));
        Set<Character> set = new HashSet<>();


        for(int i=0;i<list.size();i++)
            if(set.add(list.get(i)))set.add(list.get(i));
            else result[1]=i;

        //System.out.println(Arrays.toString(result));


        //и определяем [0] индекс в result (самое легкое):
        char repeatChar=array[result[1]].charAt(result[2]);


        for(int i=0;i<list.size();i++)
           if(list.get(i)==repeatChar){
               result[0]=i;
               return result;
           }



return result;
    }



  /*  public static void print(char[][] ch){
        for(int i=0;i< ch.length;i++) {
            for (int j = 0; j < ch[i].length; j++)
                System.out.print(ch[i][j] + " ");
            System.out.println();
        }
    }*/
}

Вывод:

Final answer:[0, 1, 2]

Process finished with exit code 0
→ Ссылка
Автор решения: MBo

Линейный по времени
(как подсказывает Stanislav Volodarskiy, вообще O(min(алфавит, N) * M)) вариант:

Создаём массив из 26 хэшмапов (индексируется c помощью символ-"a", т.е. например, для символа "z" индекс 25)

Обходим строки, для каждого символа добавляя в соответствующий ему словарь пару (позиция:номер строки), если ключа ещё нет. Если такой ключ уже есть, то решение найдено.

На Python:

def dupplaces(lst):
    maps = [{} for _ in range(26)]
    for i in range(len(lst)):  # номер строки
        for j in range(len(lst[i])):   # позиция в строке
            idx = ord(lst[i][j]) - ord('a')   #символ 'a'=0, 'b'=1...
            if j in maps[idx]:    # ключ уже есть,
                                  # для данного символа позиция j уже встречалась
                return (maps[idx][j], i, j)
            else:
                maps[idx][j] = i  
                  #символ idx зарегистрирован на j-м месте i-й строки
    return None

S = ["abc", "bea", "dbe"]
print(dupplaces(S))

>>>(0, 2, 1)    #позиция 1 совпадает в строках 0 и 2
→ Ссылка