2017-01-23 2 views
1

Я решаю проблемы с примерами, одновременно пытаясь изучить Python% \ ... но проблема, с которой я столкнулась, имеет проблемы и решения на Java, поэтому я пытаюсь преобразовать между двумя языками. Я только что узнал, как работает бит-сдвиг, и я смотрел на этот код, пытаясь понять, что именно происходит здесь в строке 5 (и 8) ... Я попытался записать несколько примеров, просто проходя через код по строкам но почему-то это все еще не совсем очевидно для меня ... Не могли бы кто-нибудь прояснить?Может ли кто-нибудь объяснить, как в этом коде используется сдвиг?

Кроме того, это действительно странно для меня, что str.charAt(i) возвращает символ, насколько я понимаю, и тогда вы можете перейти к вычесть его из другого персонажа как номера ... Является ли применение int к характеру такой же, как в ord() Python?

Проблема: Реализация алгоритма для определения строки имеет все уникальные символы.

решение (в случае только с символами A-Z):

1 boolean isUniqueChars(String str){ 
2  int checker = 0 
3  for (int i = 0; i < str.length(); i++){ 
4   int val = str.charAt(i) - 'a'; 
5   if ((checker & (1 << val)) > 0){ 
6    return false; 
7   } 
8   checker |= (1 << val); 
9  } 
10  return true; 
11 } 
+0

В Java символы представляют собой целочисленные 16-разрядные неподписанные типы и 'char-char -> int' (являющиеся целочисленной разницей между двумя [обычно кодовыми точками в BMP]). В случае Java нет «int применяется». Не удивительно, что 'str.charAt (i)' возвращает символ, потому что 1), что еще он вернет? и 2) задокументировано это. – user2864740

+0

@ пользователь2864740, о, я вижу. Это было не str.charAt (i) возвращение символа, который был запутанным :) это была следующая операция. Но эта часть имеет смысл сейчас. То, что достигается сдвигом 1 по разнице, все же ускользает от меня. – Raksha

ответ

3

Конечно. Этот код действительно будет работать, только если str имеет в нем только строчные буквы.

checker - это 32-разрядное целое число, и вы используете 26 из 32 бит для записи присутствия конкретной буквы в str. Таким образом, бит 0 будет использоваться для записи присутствия a, бит 1 будет использоваться для записи присутствия b и т. Д. До бита 25, который будет использоваться для записи присутствия z.

Основным алгоритмом является работа через str, символ по характеру. Для каждого символа найдите соответствующий бит в checker. Если он уже установлен, этот символ должен появляться во второй раз, поэтому мы можем прекратить обработку и вернуть false. В противном случае установите этот бит.

Если вы дойдете до конца строки, не найдя дубликатов символов, верните true.

Магия находится в следующих шагах.

  • Вычитание 'a' от каждого символа преобразует его в число от 0 до 25.
  • Символ << является «сдвиг влево» оператор, который перемещает битовый шаблон число битов влево. Результатом этого является то, что 1 << val - это значение места для определенного бита (1, 2, 4, 8 и т. Д.).
  • Символ & делает двоичный код AND, поэтому выражение checker & (1 << val) будет 0, если бит val очищен или равен 1 << val, если он установлен.
  • Символ |= выполняет двоичный код OR и присваивает результат переменной слева. Таким образом, выражение checker |= (1 << val) устанавливает бит val.
+0

Так просто в ретроспективе ... Но почему это более эффективно, чем использование логического массива, например, например, shmosel? – Raksha

+0

Я не знаю. Я даже не уверен, что это будет быстрее, чем использование логического массива, хотя, очевидно, оно использует меньше памяти. В любом случае, я, конечно, никогда не буду программировать сам, больше из-за отсутствия читаемости, чем что-либо еще. –

+0

это на самом деле не очевидно для меня>.> ... это потому, что мы используем int вместо массива? <. <... и почему вы говорите, что это ненадежно? – Raksha

2

Давайте попробуем более простой вариант:

boolean isUniqueChars(String str) { 
    boolean[] seen = new boolean[26]; 
    for (int i = 0; i < str.length(); i++) { 
     // convert char to 0-based offset 
     int index = str.charAt(i) - 'a'; 
     if (seen[index]) { 
      // this char was seen already 
      return false; 
     } 
     seen[index] = true; 
    } 
    // no duplicates found 
    return true; 
} 

Довольно straighforward, верно? Мы создаем логический массив, используя смещение символа как индекс, и проверяем каждый символ в строке, чтобы узнать, встретили ли мы его еще.Выставляемый вами код использует ту же логику, но вместо этого используется более эффективный бит.

(1 << val) превращает val в разрядный индекс. checker & (1 << val) отфильтровывает другие индексы, поэтому мы можем проверить только этот. (checker & (1 << val)) > 0 проверяет, есть ли значение в индексе. checker |= (1 << val) превращает бит в индекс. Вся другая логика идентична.

+0

Это точное первое решение, которое использует книга: D Предопределите массив со всеми значениями false, а затем, встретив букву, измените слот, соответствующий его ascii-коду, на «true». Но тогда это показывает, как сделать его более эффективным, превращаясь в биты, и именно там они потеряли меня. Так что это буквально та же концепция. – Raksha

2

Во-первых, код будет работать только надежно, если строка ввода содержит буквы 'a' to 'z'.

Контроллер переменной 32 бит. Каждый бит в checker используется как флаг для указания наличия одного из 26 букв «a» - «z».

Линия

int val = str.charAt(i) - 'a'; 

преобразует символ хранящийся в индексе ул я в целое число, хранящемся в вале, путем вычитания значения символа «а» так. Да, я думаю, что у pascal есть функция Ord (''), которая делает то же самое.

a = 0 
b = 1 
c = 2 
etc 
etc 
etc 
z = 25 

код

(1 << val) 

сдвиги вал в соответствующее значение для его битовой позиции,

так

a = 0 = 1 
b = 1 = 2 
c = 2 = 4 
d = 3 = 8 
etc 
etc 
z = 25 = 33554432 

if ((checker & (1 << val)) > 0) 

определяет, если бит уже установлен в чекер, указывая дублирующий символ. Если это так, функция возвращает false.

еще

checker |= (1 << val) 

устанавливает бит в проверки через бинарный или петли, а затем круглый снова.

Смежные вопросы