Лучший способ/алгоритм, чтобы узнать, состоит ли строка только из заданного набора символов
Меня попросили в интервью,
Если вы узнаете, содержит ли строка только заданный набор символов.
Например, пусть множество строк - все строки над {0,1,2,3,4,5,6,7,8,9}, т.е. все "числовые" строки. Среди этого, если множество строк над {3,8,5} является только допустимым, как проверить, содержит ли строка только допустимые символы.
Скажите:
Input 8888338385
Output VALID
Input 887837348234
Output : Invalid
Я предположил, что это грубая сила, требующая проверки каждого символа в данной строке на список недопустимых символов. Если какой-либо из символов был недействителен, я бы пропустил проверку всех других символов и отобразил сообщение об ошибке.
Однако, как предложено здесь, могут быть лучшие алгоритмы.
Пожалуйста, помогите.
Ответы
Ответ 1
РЕДАКТИРОВАТЬ: Благодаря Люку Торайлу для значительного улучшения исходного алгоритма.
Создайте массив a[10]
из булевых. Для каждой ожидаемой цифры e
установите a[e] = true
.
Теперь для каждой цифры d
на вашем входе проверьте, соответствует ли a[d]
значение true. Если это не так, верните false. Если все они успешны, верните true.
Вы можете обобщить это на все символы ASCII с массивом из 256 элементов.
Если ваша строка ввода - длина N, ваша строка сравнения - длина M, а количество букв в вашем алфавите - A, тогда сложность O (N + M) (для сканирования двух строк) плюс O (A ) (для инициализации булевого массива). Поэтому, если длина вашей строки не больше или больше, чем размер вашего алфавита, это может оказаться не оптимальным.
Стоит отметить, что в отношении Niklas Baumstark отличное сравнение производительности что наши два решения на самом деле одинаковы. Булевский массив, построенный здесь, идентичен таблице перехода, которую вы построили в двухзначном DFA, принимающем [c 1 с <суб > 2суб > ...] *. Я предполагаю, что единственное отличие заключается в том, что реализация Java, будучи намного более общей, несет намного больше накладных расходов.
Ответ 2
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: В отличие от моих предположений, Java, похоже, suck при оптимизации используемого здесь регулярного выражения, что приводит к неэффективный код. Даже регулярные выражения Javascript кажутся быстрее этого. Тест также показывает, что решение Ник очень быстро.
Это определенно задача для регулярного выражения. В Java:
public boolean isValidString(String str) {
return str.matches("[358]*");
}
Это должно быть O(n)
наихудший случай, и оно не может быть лучше, потому что каждый символ должен быть просмотрен.
Если производительность критическая, вы, вероятно, захотите кэшировать предварительно скомпилированный паттерн шаблонов:
import java.util.regex.Pattern;
public class Matcher {
private Pattern pattern;
public Matcher() {
this.pattern = Pattern.compile("[358]*");
}
public isValid(String str) {
return pattern.matcher(str).matches();
}
}
Ответ 3
Вы можете использовать карту для каждого символа в разрешенном наборе (если алфавит имеет ограниченный диапазон) и проверить непосредственно для каждого символа в строках, которые вы проверяете, если они находятся на карте. таким образом, его единственный O (N), где N - длина строки, а не O (N * M), где M - множество допустимых символов. Если алфавит имеет большой масштаб, чем другая структура данных, можно использовать для хранения разрешенных символов - отсортированное дерево, например, для сложности O (N) logN.
Ответ 4
для c или С++, вы можете сделать что-то вроде этого:
const char* haystack = "8888338385";
const char* filter = "385";
if (strlen(haystack) != strspn(haystack, filter))
{
// oops - haystack contains more characters...
}
Эквивалентные функции std::string
существуют для С++ (std::string::find_first_not_of
)
EDIT: Я понимаю, что это обман, но в вопросе, который исключает это, нет ничего.
Ответ 5
Сначала я отсортировал бы вход и список недопустимых букв, тогда вы всегда можете определить, действительно ли строка не в линейном времени