Estoy confundido acerca de cómo funcionaría un vector de bits para hacer esto (no estoy demasiado familiarizado con los vectores de bits). Aquí está el código dado. ¿Podría alguien guiarme a través de esto?
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;
}
En particular, ¿qué está checker
haciendo?
java
string
bit-manipulation
bitvector
usuario1136342
fuente
fuente
Respuestas:
int checker
se usa aquí como almacenamiento de bits. Cada bit en el valor entero se puede tratar como una bandera, por lo que eventualmenteint
es una matriz de bits (bandera). Cada bit en su código indica si el carácter con el índice del bit se encontró en cadena o no. Podría usar bit vector por la misma razón en lugar deint
. Hay dos diferencias entre ellos:Tamaño .
int
tiene un tamaño fijo, generalmente 4 bytes, lo que significa 8 * 4 = 32 bits (banderas). El vector de bits generalmente puede tener un tamaño diferente o debe especificar el tamaño en el constructor.API . Con los vectores de bits tendrá un código más fácil de leer, probablemente algo como esto:
vector.SetFlag(4, true); // set flag at index 4 as true
porque
int
tendrás un código lógico de bit de nivel inferior:checker |= (1 << 5); // set flag at index 5 to true
También es probable
int
que sea un poco más rápido, porque las operaciones con bits son de muy bajo nivel y la CPU puede ejecutarlas tal cual. BitVector permite escribir un poco menos de código críptico y además puede almacenar más banderas.Para referencia futura: el vector bit también se conoce como bitSet o bitArray. Aquí hay algunos enlaces a esta estructura de datos para diferentes idiomas / plataformas:
fuente
Tengo la sospecha de que obtuviste este código del mismo libro que estoy leyendo ... El código en sí no es tan críptico como los operadores- | =, &, y << que normalmente no usan nosotros legos: el autor no se molestó en tomarse un tiempo extra para explicar el proceso ni cuáles son los mecanismos reales involucrados aquí. Al principio estaba contento con la respuesta anterior en este hilo, pero solo en un nivel abstracto. Volví a ello porque sentí que tenía que haber una explicación más concreta: la falta de una siempre me deja con un sentimiento incómodo.
Este operador << es un desplazador de bits a la izquierda, toma la representación binaria de ese número u operando y la desplaza por todos los lugares especificados por el operando o número a la derecha como en números decimales solo en binarios. Estamos multiplicando por la base 2, cuando nos movemos hacia arriba, sin importar cuántos lugares no sean la base 10, por lo que el número a la derecha es el exponente y el número a la izquierda es un múltiplo base de 2.
Este operador | = toma el operando a la izquierda y / o está con el operando a la derecha, y este - '&' y son los bits de ambos operandos a izquierda y derecha.
Entonces, lo que tenemos aquí es una tabla hash que se almacena en un número binario de 32 bits cada vez que el verificador obtiene o'd (
checker |= (1 << val)
) con el valor binario designado de una letra, su bit correspondiente se establece en verdadero. El valor del carácter es and'd con el verificador (checker & (1 << val)) > 0
), si es mayor que 0, sabemos que tenemos una duplicación, porque dos bits idénticos establecidos en verdadero y juntos devolverán verdadero o '1' '.Hay 26 lugares binarios, cada uno de los cuales corresponde a una letra minúscula, dijo el autor para suponer que la cadena solo contiene letras minúsculas, y esto se debe a que solo tenemos 6 lugares más (en números enteros de 32 bits) para consumir, y que tener una colisión
Entonces, para una cadena de entrada 'azya', a medida que avanzamos paso a paso
cadena 'a'
cadena 'az'
cadena 'azy'
cadena 'azya'
Ahora, declara un duplicado
fuente
Creo que todas estas respuestas explican cómo funciona esto, sin embargo, tuve ganas de dar mi opinión sobre cómo lo vi mejor, renombrando algunas variables, agregando otras y agregando comentarios:
fuente
También supongo que su ejemplo proviene del libro Cracking The Code Interview y mi respuesta está relacionada con este contexto.
Para utilizar este algoritmo para resolver el problema, debemos admitir que solo vamos a pasar caracteres de la a a la z (en minúsculas).
Como solo hay 26 letras y están ordenadas correctamente en la tabla de codificación que usamos, esto nos garantiza que todas las diferencias potenciales
str.charAt(i) - 'a'
serán inferiores a 32 (el tamaño de la variable intchecker
).Como explica Snowbear, estamos a punto de usar la
checker
variable como una matriz de bits. Tengamos un enfoque por ejemplo:Digamos
str equals "test"
y así sucesivamente ... hasta que encontremos un bit ya establecido en el verificador para un personaje específico a través de la condición
Espero eso ayude
fuente
Hay un par de excelentes respuestas ya proporcionadas anteriormente. Así que no quiero repetir lo que ya ha dicho todo. Pero quería agregar un par de cosas para ayudar con el programa anterior, ya que solo trabajé en el mismo programa y tuve un par de preguntas, pero después de pasar un tiempo, tengo más claridad sobre este programa.
En primer lugar, "checker" se utiliza para rastrear el carácter que ya está atravesado en la cadena para ver si hay caracteres que se repiten.
Ahora "checker" es un tipo de datos int, por lo que solo puede tener 32 bits o 4 bytes (según la plataforma), por lo que este programa solo puede funcionar correctamente para un conjunto de caracteres dentro de un rango de 32 caracteres. Esa es la razón, este programa resta 'a' de cada carácter para hacer que este programa se ejecute solo con caracteres en minúscula. Sin embargo, si combina caracteres en mayúsculas y minúsculas, no funcionaría.
Por cierto, si no resta 'a' de cada carácter (vea la siguiente declaración), este programa funcionará correctamente solo para Cadena con caracteres en mayúscula o Cadena con solo caracteres en minúscula. Por lo tanto, el alcance del programa anterior aumenta de caracteres en minúsculas a caracteres en mayúsculas también, pero no se pueden mezclar.
Sin embargo, quería escribir un programa genérico usando Bitwise Operation que debería funcionar para cualquier carácter ASCII sin preocuparme por mayúsculas, minúsculas, números o cualquier carácter especial. Para hacer esto, nuestro "verificador" debe ser lo suficientemente grande como para almacenar 256 caracteres (tamaño del conjunto de caracteres ASCII). Pero un int en Java no funcionaría, ya que solo puede almacenar 32 bits. Por lo tanto, en el siguiente programa, estoy usando la clase BitSet disponible en JDK, que puede pasar cualquier tamaño definido por el usuario al crear instancias de un objeto BitSet.
Aquí hay un programa que hace lo mismo que el programa anterior escrito con el operador Bitwise, pero este programa funcionará para una cadena con cualquier carácter del conjunto de caracteres ASCII.
fuente
for(int i = 0; i < s.length(); i++) { int charVal = s.charAt(i); if(tracker.get(charVal)) { return false; } tracker.set(charVal); }
Leer la respuesta de Ivan arriba realmente me ayudó, aunque lo expresaría de manera algo diferente.
El
<<
en(1 << val)
es un operador de desplazamiento de bits. Toma1
(que en binario se representa como000000001
, con tantos ceros anteriores como desee / son asignados por la memoria) y lo desplaza a la izquierda porval
espacios. Dado que estamos asumiendo solo az y restandoa
cada vez, cada letra tendrá un valor de 0-25, que será el índice de esa letra desde la derecha en lachecker
representación booleana del entero, ya que nos desplazaremos1
hacia la izquierda enchecker
val
ocasiones.Al final de cada verificación, vemos al
|=
operador. Esto combina dos números binarios, reemplazando todos los0
's con1
' s si1
existe uno en cualquiera de los operandos en ese índice. Aquí, eso significa que donde sea que1
exista un(1 << val)
,1
se copiará enchecker
, mientras que todoschecker
los 1 existentes se conservarán.Como probablemente pueda adivinar, un
1
aquí funciona como un indicador booleano para verdadero. Cuando verificamos si un carácter ya está representado en la cadena, comparamoschecker
, que en este punto es esencialmente una matriz de banderas booleanas (1
valores) en los índices de caracteres que ya han sido representados, con lo que es esencialmente una matriz de valores booleanos con una1
bandera en el índice del carácter actual.El
&
operador realiza esta verificación. Similar al|=
, el&
operador copiará1
solo si ambos operandos tienen un1
índice en ese índice. Entonces, esencialmente, solo se copiarán las banderas ya presentes en laschecker
que también estén representadas(1 << val)
. En este caso, eso significa que solo si el carácter actual ya ha sido representado habrá un1
presente en cualquier parte del resultado dechecker & (1 << val)
. Y si a1
está presente en alguna parte del resultado de esa operación, entonces el valor del booleano devuelto es> 0
, y el método devuelve falso.Esto es, supongo, por qué los vectores de bits también se llaman matrices de bits . Porque, aunque no son del tipo de datos de matriz, se pueden usar de forma similar a la forma en que se usan las matrices para almacenar banderas booleanas.
fuente
Explicación simple (con el código JS a continuación)
32-bit
DEC64
para JS.0th
índice si lo encontramosa
en la cadena,1st
parab
y así sucesivamente.Resumen de operaciones:
checker
yindex
del personajeInt-32-Arrays
if
la salida de la operación fue1
output == 1
checker
variable tiene ese índice-bit particular establecido en ambas matricesoutput == 0
checker
yindex
del personaje1
checker
Suposiciones
a
is97
A continuación se muestra el código fuente de JavaScript .
Tenga en cuenta que en JS, a pesar de que los enteros son de 64 bits, una operación inteligente se realiza siempre en 32 bits.
Ejemplo: si la cadena es
aa
entonces:i = 0
i = 1
fuente
Vamos a desglosar el código línea por línea.
int checker = 0; Estamos iniciando un verificador que nos ayudará a encontrar valores duplicados.
int val = str.charAt (i) - 'a'; Estamos obteniendo el valor ASCII del carácter en la 'i' posición de la cadena y restandolo con el valor ASCII de 'a'. Como se supone que la cadena tiene solo caracteres inferiores, el número de caracteres está limitado a 26. Hece, el valor de 'val' siempre será> = 0.
if ((checker & (1 << val))> 0) devuelve falso;
corrector | = (1 << val);
Ahora esta es la parte difícil. Consideremos un ejemplo con la cadena "abcda". Idealmente, esto debería devolver falso.
Para la iteración de bucle 1:
Verificador: 00000000000000000000000000000000
val: 97-97 = 0
1 << 0: 00000000000000000000000000000001
corrector & (1 << val): 00000000000000000000000000000000 no es> 0
Por lo tanto, corrector: 00000000000000000000000000000001
Para la iteración de bucle 2:
Comprobador: 00000000000000000000000000000001
val: 98-97 = 1
1 << 0: 00000000000000000000000000000010
corrector & (1 << val): 00000000000000000000000000000000 no es> 0
Por lo tanto, corrector: 00000000000000000000000000000011
Para la iteración de bucle 3:
Verificador: 00000000000000000000000000000011
val: 99-97 = 0
1 << 0: 00000000000000000000000000000100
corrector & (1 << val): 00000000000000000000000000000000 no es> 0
Por lo tanto, corrector: 00000000000000000000000000000111
Para la iteración de bucle 4:
Verificador: 00000000000000000000000000000111
val: 100-97 = 0
1 << 0: 00000000000000000000000000001000
corrector & (1 << val): 00000000000000000000000000000000 no es> 0
Por lo tanto, corrector: 00000000000000000000000000001111
Para la iteración de bucle 5:
Comprobador: 00000000000000000000000000001111
val: 97-97 = 0
1 << 0: 00000000000000000000000000000001
corrector y (1 << val): 00000000000000000000000000000001 es> 0
Por lo tanto, devuelve falso.
fuente
fuente
Las publicaciones anteriores explican bien lo que hace el bloque de código y quiero agregar mi solución simple usando la estructura de datos BitSet java:
fuente
La forma en que entendí usando Javascript. Asumiendo entrada
var inputChar = "abca"; //find if inputChar has all unique characters
Empecemos
Line 4: int val = str.charAt(i) - 'a';
En Javascript Ej .:
"a".charCodeAt().toString(2)
devuelve 1100001checker = 1100001 | checker;
// el corrector se convierte en 1100001 (en la representación de 32 bits se convierte en 000000000 ..... 00001100001)Pero quiero que mi bitmask (
int checker
) establezca solo un bit, pero el verificador es 1100001Permite el uso
val
que se restableceLa línea 5 y la línea 6 están bien explicadas @Ivan answer
fuente
Solo en caso de que alguien esté buscando kotlin equivalente de caracteres únicos en una cadena usando un vector de bits
Ref: https://www.programiz.com/kotlin-programming/bitwise
fuente