Интервью Google: найти сумасшедшее расстояние между строками
Этот вопрос был задан мне в интервью Google. Я мог бы сделать это O (n * n)... Могу ли я сделать это в лучшем случае.
Строку можно сформировать только на 1 и 0.
Определение:
X и Y - строки, образованные 0 или 1
D(X,Y)
= Удалите вещи, которые обычно начинаются с X и Y. Затем добавьте оставшиеся длины из обеих строк.
Например,
D(1111, 1000)
= Только первый алфавит является общим. Таким образом, оставшаяся строка 111
и 000
. Поэтому результат length("111")
и length("000")
= 3 + 3 = 6
D(101, 1100)
= Только первые два алфавита являются общими. Таким образом, оставшаяся строка 01
и 100
. Поэтому результат length("01")
и length("100")
= 2 + 3 = 5
Довольно очевидно, что найти такое сумасшедшее расстояние будет линейным. O (м).
Теперь вопрос
для ввода n, например,
1111
1000
101
1100
Узнайте максимальное возможное расстояние.
n - количество входных строк.
m - максимальная длина любой входной строки.
Решение O (n 2 * m) довольно просто. Можно ли это сделать лучше?
Предположим, что m фиксировано. Можем ли мы сделать это лучше, чем O (n ^ 2)?
Ответы
Ответ 1
Поместите строки в дерево, где 0 означает идти влево, а 1 означает идти вправо. Так, например,
1111
1000
101
1100
приведет к дереву, подобному
Root
1
0 1
0 1* 0 1
0* 0* 1*
где * означает, что элемент заканчивается там. Построение этого дерева явно занимает O(n m)
.
Теперь мы должны найти диаметр дерева (самый длинный путь между двумя узлами, что является тем же самым, что и "сумасшедшее расстояние" ). Оптимизированный алгоритм, представленный там, попадает в каждый node в дереве один раз. Существует не более min(n m, 2^m)
таких узлов.
Итак, если n m < 2^m
, то алгоритм O(n m)
.
Если n m > 2^m
(и мы обязательно имеем повторяющиеся входы), то алгоритм все еще O(n m)
с первого шага.
Это также работает для строк с общим алфавитом; для алфавита с буквами k
строят дерево k
-ary, в этом случае время выполнения по-прежнему равно O (n m) по тем же аргументам, хотя оно занимает k
раз больше памяти.
Ответ 2
Я думаю, что это возможно в O (нм) времени, создавая двоичное дерево, где каждый бит в строке кодирует путь (0 слева, 1 справа). Затем найдите максимальное расстояние между узлами дерева которое можно выполнить в O (n) времени.
Ответ 3
Это мое решение, я думаю, он работает:
-
Создайте двоичное дерево из всех строк. Дерево будет построено таким образом:
в каждом раунде выберите строку и добавьте ее в дерево. поэтому для вашего примера дерево будет:
<root>
<1> <empty>
<1> <0>
< 1 < 0 < 1 <0 > < 1 < 0 <0 >
Таким образом, каждый путь от корня до листа будет представлять собой строку.
- Теперь расстояние между каждыми двумя листьями - это расстояние между двумя строками. Чтобы найти сумасшедшее расстояние, вы должны найти диаметр этого графика, что вы можете сделать это легко с помощью dfs или bfs.
Общая сложность этого алгоритма:
O (n * m) + O (n * m) = O (n * m).
Ответ 4
Чтобы получить ответ в O (nm), просто перебирайте символы всей строки (это операция O (n)). Мы сравним не более m символов, так что это будет сделано O (m). Это дает общее количество O (нм). Здесь С++-решение:
int max_distance(char** strings, int numstrings, int &distance) {
distance = 0;
// loop O(n) for initialization
for (int i=0; i<numstrings; i++)
distance += strlen(strings[i]);
int max_prefix = 0;
bool done = false;
// loop max O(m)
while (!done) {
int c = -1;
// loop O(n)
for (int i=0; i<numstrings; i++) {
if (strings[i][max_prefix] == 0) {
done = true; // it is enough to reach the end of one string to be done
break;
}
int new_element = strings[i][max_prefix] - '0';
if (-1 == c)
c = new_element;
else {
if (c != new_element) {
done = true; // mismatch
break;
}
}
}
if (!done) {
max_prefix++;
distance -= numstrings;
}
}
return max_prefix;
}
void test_misc() {
char* strings[] = {
"10100",
"10101110",
"101011",
"101"
};
std::cout << std::endl;
int distance = 0;
std::cout << "max_prefix = " << max_distance(strings, sizeof(strings)/sizeof(strings[0]), distance) << std::endl;
}
Ответ 5
Я думаю, что эта проблема похожа на "найти префикс для двух строк", вы можете использовать trie (http://en.wikipedia.org/wiki/Trie), чтобы ускорить поиск
У меня есть телефонное интервью в Google за 3 дня до этого, но, возможно, я потерпел неудачу...
Удачи вам
Ответ 6
Не знаю, почему использовать деревья, когда итерация дает вам такую же большую вычислительную сложность O без сложности кода. в любом случае здесь моя версия этого в javascript O (mn)
var len = process.argv.length -2; // in node first 2 arguments are node and program file
var input = process.argv.splice(2);
var current;
var currentCount = 0;
var currentCharLoc = 0;
var totalCount = 0;
var totalComplete = 0;
var same = true;
while ( totalComplete < len ) {
current = null;
currentCount = 0;
for ( var loc = 0 ; loc < len ; loc++) {
if ( input[loc].length === currentCharLoc) {
totalComplete++;
same = false;
} else if (input[loc].length > currentCharLoc) {
currentCount++;
if (same) {
if ( current === null ) {
current = input[loc][currentCharLoc];
} else {
if (current !== input[loc][currentCharLoc]) {
same = false;
}
}
}
}
}
if (!same) {
totalCount += currentCount;
}
currentCharLoc++;
}
console.log(totalCount);