Я не понимаю, как будет работать битовый вектор (не слишком знаком с битовыми векторами). Вот код, данный. Может кто-нибудь, пожалуйста, проведите меня через это?
public static boolean isUniqueChars(String str) {
int checker = 0;
for (int i = 0; i < str.length(); ++i) {
int val = str.charAt(i) - 'a';
if ((checker & (1 << val)) > 0) return false;
checker |= (1 << val);
}
return true;
}
В частности, что checker
делает?
java
string
bit-manipulation
bitvector
user1136342
источник
источник
Ответы:
int checker
используется здесь как хранилище для битов. Каждый бит в целочисленном значении можно рассматривать как флаг, поэтому в конечном итогеint
это массив битов (флаг). Каждый бит в вашем коде указывает, был ли найден символ с индексом бита в строке или нет. Вы можете использовать битовый вектор по той же причине вместоint
. Между ними есть два отличия:Размер .
int
имеет фиксированный размер, обычно 4 байта, что означает 8 * 4 = 32 бита (флаги). Битовый вектор обычно может быть разного размера, или вы должны указать размер в конструкторе.API . С битовыми векторами вам будет легче читать код, вероятно, что-то вроде этого:
vector.SetFlag(4, true); // set flag at index 4 as true
у
int
вас будет битовый логический код более низкого уровня:checker |= (1 << 5); // set flag at index 5 to true
Также, вероятно,
int
может быть немного быстрее, потому что операции с битами очень низкого уровня и могут быть выполнены как есть CPU. BitVector позволяет писать чуть меньше загадочного кода, а также может хранить больше флагов.Для дальнейшего использования: битовый вектор также известен как bitSet или bitArray. Вот несколько ссылок на эту структуру данных для разных языков / платформ:
источник
У меня есть подозрение, что вы получили этот код из той же книги, которую я читаю ... Сам код здесь не настолько загадочный, как операторы- | =, &, <<, которые обычно не используются мы, неспециалист, - автор не удосужился потратить дополнительное время на объяснение процесса и на то, какова действительная механика, которая здесь задействована. Я был доволен предыдущим ответом на эту тему в начале, но только на абстрактном уровне. Я вернулся к этому, потому что чувствовал, что нужно более конкретное объяснение - его отсутствие всегда вызывает у меня неприятное чувство.
Этот оператор << является левым побитовым сдвигом, он принимает двоичное представление этого числа или операнда и сдвигает его на любое количество мест, указанных операндом или числом справа, как в десятичных числах только в двоичных файлах. Мы умножаем на основание 2 - когда мы двигаемся вверх, но во многих местах не основание 10 - так что число справа - это показатель степени, а число слева - это основание, кратное 2.
Этот оператор | = берет операнд слева и / или его с операндом справа - и этот - '&' и биты обоих операндов слева и справа от него.
Итак, у нас есть хеш-таблица, которая хранится в 32-битном двоичном числе каждый раз, когда проверяющий получает or'd (
checker |= (1 << val)
) с заданным двоичным значением буквы, соответствующий его бит, для которого устанавливается значение true. Значение символа равно and'd с помощью checker (checker & (1 << val)) > 0
) - если оно больше 0, мы знаем, что у нас есть дублирование, - потому что два идентичных бита, установленные в true и имеющие вместе, вернут true или '1' '.Есть 26 двоичных разрядов, каждое из которых соответствует строчной букве - автор сказал, что строка содержит только строчные буквы - и это потому, что у нас осталось только 6 (в 32-битном целом) месте для использования - и чем мы столкнуться
Итак, для входной строки 'azya', по мере продвижения по шагам
строка «а»
строка 'az'
строка 'azy'
Строка 'Azya'
Теперь он объявляет дубликат
источник
Я думаю, что все эти ответы объясняют, как это работает, однако мне хотелось высказать свое мнение о том, как я это видел, переименовав некоторые переменные, добавив некоторые другие и добавив к ним комментарии:
источник
Я также предполагаю, что ваш пример взят из книги « Взлом кода», и мой ответ связан с этим контекстом.
Чтобы использовать этот алгоритм для решения проблемы, мы должны признать, что мы собираемся передавать только символы от a до z (строчные буквы).
Поскольку существует только 26 букв, и они правильно отсортированы в используемой нами таблице кодирования, это гарантирует нам, что все потенциальные различия
str.charAt(i) - 'a'
будут меньше 32 (размер переменной intchecker
).Как объяснил Snowbear, мы собираемся использовать
checker
переменную как массив битов. Давайте рассмотрим пример:Скажем
str equals "test"
и так далее .. пока мы не найдем уже установленный бит в контролере для конкретного символа через условие
Надеюсь, поможет
источник
Есть несколько отличных ответов, уже предоставленных выше. Поэтому я не хочу повторять то, что уже все сказано. Но я хотел добавить пару вещей, чтобы помочь с вышеупомянутой программой, так как я только что работал над той же самой программой и у меня было несколько вопросов, но потратив некоторое время, у меня есть больше ясности в этой программе.
Прежде всего, «проверка» используется для отслеживания символа, который уже пройден в строке, чтобы увидеть, есть ли повторяющиеся символы.
Теперь «checker» является типом данных int, поэтому он может иметь только 32 бита или 4 байта (в зависимости от платформы), поэтому эта программа может корректно работать только для набора символов в диапазоне 32 символа. По этой причине эта программа вычитает «a» из каждого символа, чтобы эта программа работала только для строчных букв. Однако если вы смешаете символы нижнего и верхнего регистров, это не сработает.
Кстати, если вы не вычитаете «a» из каждого символа (см. Оператор ниже), то эта программа будет работать правильно только для строки с символами верхнего регистра или строки только с символами нижнего регистра. Таким образом, область действия вышеуказанной программы увеличивается от букв нижнего регистра до символов верхнего регистра, но их нельзя смешивать вместе.
Однако я хотел написать общую программу с использованием побитовой операции, которая должна работать с любыми символами ASCII, не беспокоясь о верхнем регистре, нижнем регистре, числах или любых специальных символах. Для этого наш «чекер» должен быть достаточно большим для хранения 256 символов (размер набора символов ASCII). Но int в Java не будет работать, поскольку он может хранить только 32 бита. Следовательно, в приведенной ниже программе я использую класс BitSet, доступный в JDK, который может передавать любой определенный пользователем размер при создании экземпляра объекта BitSet.
Вот программа, которая делает то же самое, что и предыдущая программа, написанная с использованием побитового оператора, но эта программа будет работать для строки с любым символом из набора символов ASCII.
источник
for(int i = 0; i < s.length(); i++) { int charVal = s.charAt(i); if(tracker.get(charVal)) { return false; } tracker.set(charVal); }
Чтение ответа Ивана выше действительно помогло мне, хотя я бы сформулировал это несколько иначе.
<<
В(1 << val)
это бит - оператор сдвига. Он берет1
(который в двоичном виде представлен как000000001
, с тем количеством предшествующих нулей, сколько вам нравится / выделяется памятью) и сдвигает его влево наval
пробелы. Поскольку мы предполагаем только az и вычитаемa
каждый раз, каждая буква будет иметь значение 0-25, которое будет индексом этой буквы справа вchecker
логическом представлении целого числа, поскольку мы будем перемещать1
влево вchecker
val
разы.В конце каждой проверки мы видим
|=
оператора. Это объединяет два двоичных числа, заменяя все0
на s1
, если a1
существует в любом операнде с этим индексом. Здесь это означает, что там, где1
существует a(1 << val)
, оно1
будет скопированоchecker
, а всеchecker
существующие 1 будут сохранены.Как вы, вероятно, можете догадаться, здесь
1
функции в качестве логического флага для истины. Когда мы проверяем, представлен ли уже символ в строке, мы сравниваемchecker
, который на данный момент является массивом логических флагов (1
значений) по индексам символов, которые уже были представлены, с тем, что по сути является массивом логические значения с1
флагом в индексе текущего символа.&
Оператор выполняет эту проверку. Аналогично|=
,&
оператор будет копировать1
только если оба операнда имеют1
по этому индексу. Таким образом, по сути, будут скопированы только те флаги, которые уже присутствуют вchecker
них, также представленные в(1 << val)
. В этом случае это означает, что только если текущий символ уже был представлен, будет1
присутствовать где-нибудь в результатеchecker & (1 << val)
. И если1
где-либо в результате этой операции присутствует a , то значение возвращаемого логического значения равно> 0
, и метод возвращает false.Я догадываюсь, почему битовые векторы также называются битовыми массивами . Потому что, хотя они не относятся к типу данных массива, их можно использовать аналогично тому, как массивы используются для хранения логических флагов.
источник
Простое объяснение (с кодом JS ниже)
32-bit
DEC64
для JS.0th
индекс , если мы находимa
в строке,1st
дляb
и так далее.Краткое описание операций:
checker
&index
персонажаInt-32-Arrays
if
вывод операции был1
output == 1
checker
переменная имеет определенный индексный бит, установленный в обоих массивахoutput == 0
checker
&index
символа1
checker
Предположения:
a
is97
Ниже приведен исходный код JavaScript .
Обратите внимание, что в JS, несмотря на то, что целые числа имеют 64 бита, битовая операция всегда выполняется на 32 битах.
Пример: если строка
aa
то:я = 0
я = 1
источник
Давайте разберем код построчно.
int checker = 0; Мы инициируем проверку, которая поможет нам найти повторяющиеся значения.
int val = str.charAt (i) - 'a'; Мы получаем значение ASCII символа в i-й позиции строки и вычитаем его из значения ASCII 'a'. Поскольку предполагается, что строка содержит только символы младшего разряда, количество символов ограничено 26. Таким образом, значение 'val' всегда будет> = 0.
if ((checker & (1 << val))> 0) возвращает false;
проверка | = (1 << val);
Теперь это сложная часть. Давайте рассмотрим пример со строкой «abcda». В идеале это должно вернуть false.
Для цикла итерации 1:
Checker: 00000000000000000000000000000000
val: 97-97 = 0
1 << 0: 00000000000000000000000000000001
checker & (1 << val): 00000000000000000000000000000000 не> 0
Отсюда проверка: 00000000000000000000000000000001
Для цикла итерации 2:
Checker: 00000000000000000000000000000001
val: 98-97 = 1
1 << 0: 00000000000000000000000000000010
checker & (1 << val): 00000000000000000000000000000000 не> 0
Отсюда проверка: 00000000000000000000000000000011
Для цикла итерации 3:
Checker: 00000000000000000000000000000011
val: 99-97 = 0
1 << 0: 00000000000000000000000000000100
checker & (1 << val): 00000000000000000000000000000000 не> 0
Отсюда проверка: 00000000000000000000000000000111
Для цикла итерации 4:
Checker: 00000000000000000000000000000111
val: 100-97 = 0
1 << 0: 00000000000000000000000000001000
checker & (1 << val): 00000000000000000000000000000000 не> 0
Отсюда проверка: 00000000000000000000000000001111
Для цикла итерации 5:
Checker: 00000000000000000000000000001111
val: 97-97 = 0
1 << 0: 00000000000000000000000000000001
checker & (1 << val): 00000000000000000000000000000001 равно> 0
Следовательно, верните ложь.
источник
источник
Предыдущие сообщения хорошо объясняют, что делает блок кода, и я хочу добавить свое простое решение с использованием структуры данных BitSet java:
источник
Как я понял, используя Javascript. Предполагая ввод
var inputChar = "abca"; //find if inputChar has all unique characters
Давайте начнем
Line 4: int val = str.charAt(i) - 'a';
В JavaScript, например:
"a".charCodeAt().toString(2)
возвращает 1100001checker = 1100001 | checker;
// проверка становится 1100001 (в 32-битном представлении она становится 000000000 ..... 00001100001)Но я хочу, чтобы моя битовая маска (
int checker
) устанавливала только один бит, а проверка - 1100001Позволяет использовать
val
который сбрасываетсяСтрока 5 и Строка 6 хорошо объяснены @Ivan ответ
источник
На всякий случай, если кто-то ищет kotlin-эквивалент уникальных символов в строке, используя битовый вектор
Ссылка: https://www.programiz.com/kotlin-programming/bitwise
источник