Найти символы в одной позиции в массиве строк
Попался алгоритм, ничего даже приблизительно похожего на ответ выдавить из себя не смог, так что только задача.
Дан массив 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]
Напишите эффективный алгоритм для следующих предположений:
Nэтоintв диапазоне [1..30000];Mэтоintв диапазоне [1..2000];- Каждый элемент
Sсостоит только из строчных латинских букв (a-z); - N * M < 30 000.
Ответы (2 шт):
Решил так: (вышло довольно "топорно - на пролом", но все примеры из вашего вопроса проходит правильно). Я добавлял комментарии по ходу кода, но если какой-то пункт не понятен, то спрашивайте. Фактически я задачу разделил на 3 задачи: каждая задача - это вычисление одного из элементов массива на выходе. Начал с последнего элемента и закончил нулевым.
1)Поиск индекса который повторяется. Учитывая все стринги в массиве по условию задачи имеют одинаковую длину, я добавляю каждый 0 символ в LIST (проверяя нет ли такого же символа в листе). И если все добавились, перехожу на следующий символ и добавляю 1 символ со всех стрингов. И так пока, не обнаружу позицию в стрингах, в которых повторяется символ. И записываю ее в result[2]. теперь мы знаем в каком месте искать повторения.
- зная позицию в Стрингах, в которых нужно искать повторения - воспользуемся множеством 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
Линейный по времени
(как подсказывает 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