모든 문자가 고유한지 확인하기 위해 비트 벡터 사용을 설명하십시오.


150

비트 벡터가 어떻게 작동하는지 혼란 스럽습니다 (비트 벡터에 익숙하지 않음). 주어진 코드는 다음과 같습니다. 누군가 나를 통해 안내해 주시겠습니까?

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로되어 있지만 C / C ++에 비슷한 것이 있으면 더 도움이 될 것입니다.
user1136342

101
이 코드는 Cracking The Code Interview
Dejell에서 작성되었습니다.

2
이것을 테스트 했습니까? 0으로 설정되어 있기 때문에 중복 'a'문자를 감지하지 못하고 왼쪽으로 이동해도 여전히 0으로 유지됩니다.
Riz

3
이 솔루션은 더 낮은 문자 az에 사용됩니다. 즉, 26 자에 대한 중복을 찾는 데 사용됩니다. 따라서 int 32 비트를 사용할 수 있습니다. 범위가 더 크면 솔루션이 작동하지 않습니다.
a3.14_Infinity

1
사람들이 실수를 저지르는 위치는 왼쪽 시프트 연산자 구문과 혼동된다는 것입니다 .1은 왼쪽으로 x (= str.charAt (i)- 'a')만큼 왼쪽으로 이동하고 NOT x의 비트는 1 씩 왼쪽으로 이동합니다.
nanosoft

답변:


100

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라고도합니다. 다른 언어 / 플랫폼에 대한이 데이터 구조에 대한 링크는 다음과 같습니다.


Java에 BitVector 클래스가 있습니까? 문서를 찾을 수 없습니다!
Dejell

크기는 32 비트 인 고정 크기입니다. 그것은 32 문자의 유일한 테스트 만 할 수 있다는 것을 의미합니까? 이 함수는 "abcdefgZZ"가 false인지 테스트 할 수 있지만 "abcdefg @@"는 true를 반환합니다.
tli2020

1
구글이 나를 이끌었다. @Dejel 다음은 사용할 수있는 Java 데이터 구조입니다. docs.oracle.com/javase/7/docs/api/java/util/BitSet.html . 바라건대 이것은 누군가가 튜브를 통해 여행하는 데 도움이되기를 바랍니다.
nattyddubbs

@nattyddubbs, 감사합니다, 나는 이것에 대한 답변과 몇 가지 다른 링크를 추가했습니다
Snowbear

223

나는 당신이 읽고있는 동일한 책 에서이 코드를 얻었다는 의심을 가지고 있습니다 ... 여기서 코드 자체는 연산자-| =, & 및 <<만큼 일반적으로 사용되지 않습니다. 우리 평신도-저자는 과정과 여기에 포함 된 실제 기계가 무엇인지 설명하는 데 시간이 더 걸리지 않았습니다. 처음에는이 스레드에 대한 이전 답변에 만족했지만 추상 수준에서만 만족했습니다. 좀 더 구체적인 설명이 필요하다고 느꼈기 때문에 다시 돌아 왔습니다. 하나가 없으면 항상 불안한 느낌이 듭니다.

이 연산자 <<는 왼쪽 비트 단위 시프터로 해당 숫자 또는 피연산자의 이진 표현을 취하고 오른쪽에있는 피연산자 또는 숫자로 지정된 많은 위치를 이진수로만 십진수로 이동합니다. 우리는 밑이 10이 아닌 많은 장소로 올라갈 때 밑이 2를 곱합니다. 그래서 오른쪽의 숫자는 지수이고 왼쪽의 숫자는 2의 기본 배수입니다.

이 연산자 | =는 왼쪽에있는 피연산자를 가져 오거나 오른쪽에있는 피연산자와 함께 있고이 연산자는 왼쪽과 오른쪽에있는 두 피연산자의 비트입니다.

그래서 우리가 가진 것은 체커가 checker |= (1 << val)문자의 지정된 이진 값으로 해당 비트를 true로 설정 하거나 검사 할 때마다 32 비트 이진수로 저장되는 해시 테이블 입니다. 캐릭터의 값은 체커 ( checker & (1 << val)) > 0) 와 함께 사용됩니다. 0보다 크면 두 개의 동일한 비트가 true로 설정되고 함께 'd'가 true 또는 '1'을 반환하기 때문에 듀프가 있음을 알 수 있습니다.

26 개의 이진 자리가 각각 소문자에 해당합니다. 저자는 문자열에 소문자 만 포함한다고 가정했습니다. 이것은 32 비트 정수로 6 자리 만 더 남아 있기 때문입니다. 충돌하다

00000000000000000000000000000001 a 2^0

00000000000000000000000000000010 b 2^1

00000000000000000000000000000100 c 2^2

00000000000000000000000000001000 d 2^3

00000000000000000000000000010000 e 2^4

00000000000000000000000000100000 f 2^5

00000000000000000000000001000000 g 2^6

00000000000000000000000010000000 h 2^7

00000000000000000000000100000000 i 2^8

00000000000000000000001000000000 j 2^9

00000000000000000000010000000000 k 2^10

00000000000000000000100000000000 l 2^11

00000000000000000001000000000000 m 2^12

00000000000000000010000000000000 n 2^13

00000000000000000100000000000000 o 2^14

00000000000000001000000000000000 p 2^15

00000000000000010000000000000000 q 2^16

00000000000000100000000000000000 r 2^17

00000000000001000000000000000000 s 2^18

00000000000010000000000000000000 t 2^19

00000000000100000000000000000000 u 2^20

00000000001000000000000000000000 v 2^21

00000000010000000000000000000000 w 2^22

00000000100000000000000000000000 x 2^23

00000001000000000000000000000000 y 2^24

00000010000000000000000000000000 z 2^25

따라서 입력 문자열 'azya'의 경우 단계별로 이동할 때

문자열 'a'

a      =00000000000000000000000000000001
checker=00000000000000000000000000000000

checker='a' or checker;
// checker now becomes = 00000000000000000000000000000001
checker=00000000000000000000000000000001

a and checker=0 no dupes condition

문자열 'az'

checker=00000000000000000000000000000001
z      =00000010000000000000000000000000

z and checker=0 no dupes 

checker=z or checker;
// checker now becomes 00000010000000000000000000000001  

문자열 '아지'

checker= 00000010000000000000000000000001    
y      = 00000001000000000000000000000000 

checker and y=0 no dupes condition 

checker= checker or y;
// checker now becomes = 00000011000000000000000000000001

문자열 '아자'

checker= 00000011000000000000000000000001
a      = 00000000000000000000000000000001

a and checker=1 we have a dupe

이제 중복을 선언합니다


@ ivan-tichy 이것을 테스트 했습니까? 0으로 설정되어 있기 때문에 중복 'a'문자를 감지하지 못하고 왼쪽으로 이동해도 여전히 0으로 유지됩니다.
Riz

1
@ Riz No, 항상 '1'로 시작하며 알고리즘은 문자를 기준으로 1을 이동합니다. 따라서 문자 'a'가 한 번 오는 경우 1이되고 (.... 000001)이됩니다.
Taylor Halliday

2
@Ivan Man, 나는 같은 생각을하고있었습니다. 선택된 답변조차도 운영자에 대해 설명하지 않았습니다. 자세한 정보를 주셔서 감사합니다.
WowBow

위의 고유 검사가 정렬 된 문자 세트 (abcd ... z)에서만 작동한다고 가정해야합니까? with (bcad ...)
abdul rashid

"나는 당신이 내가 읽고있는 같은 책에서이 코드를 얻었다는 몰래 의심을 품고있다": 여기 동일) :) 나를 웃게했다
등뼈

39

나는이 모든 대답이 이것이 어떻게 작동하는지 설명한다고 생각하지만, 변수의 이름을 바꾸고 다른 변수를 추가하고 주석을 추가하여 더 잘 본 방법에 대한 정보를 제공하는 것처럼 느꼈습니다.

public static boolean isUniqueChars(String str) {

    /*
    checker is the bit array, it will have a 1 on the character index that
    has appeared before and a 0 if the character has not appeared, you
    can see this number initialized as 32 0 bits:
    00000000 00000000 00000000 00000000
     */
    int checker = 0;

    //loop through each String character
    for (int i = 0; i < str.length(); ++i) {
        /*
        a through z in ASCII are charactets numbered 97 through 122, 26 characters total
        with this, you get a number between 0 and 25 to represent each character index
        0 for 'a' and 25 for 'z'

        renamed 'val' as 'characterIndex' to be more descriptive
         */
        int characterIndex = str.charAt(i) - 'a'; //char 'a' would get 0 and char 'z' would get 26

        /*
        created a new variable to make things clearer 'singleBitOnPosition'

        It is used to calculate a number that represents the bit value of having that 
        character index as a 1 and the rest as a 0, this is achieved
        by getting the single digit 1 and shifting it to the left as many
        times as the character index requires
        e.g. character 'd'
        00000000 00000000 00000000 00000001
        Shift 3 spaces to the left (<<) because 'd' index is number 3
        1 shift: 00000000 00000000 00000000 00000010
        2 shift: 00000000 00000000 00000000 00000100
        3 shift: 00000000 00000000 00000000 00001000

        Therefore the number representing 'd' is
        00000000 00000000 00000000 00001000

         */
        int singleBitOnPosition = 1 << characterIndex;

        /*
        This peforms an AND between the checker, which is the bit array
        containing everything that has been found before and the number
        representing the bit that will be turned on for this particular
        character. e.g.
        if we have already seen 'a', 'b' and 'd', checker will have:
        checker = 00000000 00000000 00000000 00001011
        And if we see 'b' again:
        'b' = 00000000 00000000 00000000 00000010

        it will do the following:
        00000000 00000000 00000000 00001011
        & (AND)
        00000000 00000000 00000000 00000010
        -----------------------------------
        00000000 00000000 00000000 00000010

        Since this number is different than '0' it means that the character
        was seen before, because on that character index we already have a 
        1 bit value
         */
        if ((checker & singleBitOnPosition) > 0) {
            return false;
        }

        /* 
        Remember that 
        checker |= singleBitOnPosition is the same as  
        checker = checker | singleBitOnPosition
        Sometimes it is easier to see it expanded like that.

        What this achieves is that it builds the checker to have the new 
        value it hasnt seen, by doing an OR between checker and the value 
        representing this character index as a 1. e.g.
        If the character is 'f' and the checker has seen 'g' and 'a', the 
        following will happen

        'f' = 00000000 00000000 00000000 00100000
        checker(seen 'a' and 'g' so far) = 00000000 00000000 00000000 01000001

        00000000 00000000 00000000 00100000
        | (OR)
        00000000 00000000 00000000 01000001
        -----------------------------------
        00000000 00000000 00000000 01100001

        Therefore getting a new checker as 00000000 00000000 00000000 01100001

         */
        checker |= singleBitOnPosition;
    }
    return true;
}

2
좋은 설명입니다. 감사합니다!
Hormigas

명확한 설명 .. 감사합니다
Prabhaker A

좋은 설명입니다. 이해하기 쉬운. 감사합니다
Anil Kumar

하나는 최고라고
블라디미르 나보코프

이것이 주석이 발명 된 이유입니다.
Mr. Suryaa Jha

30

또한 귀하의 예가 Cracking The Code Interview 책에서 나온 것으로 가정 하고 내 대답은이 맥락과 관련이 있습니다.

이 알고리즘을 사용하여 문제를 해결하려면 문자를 a에서 z (소문자)로만 전달한다는 점을 인정해야합니다.

26 개의 문자 만 있고 이들이 사용하는 인코딩 테이블에서 올바르게 정렬되었으므로 모든 잠재적 차이 str.charAt(i) - 'a'가 32 (int 변수의 크기)보다 열등하지 checker않습니다.

Snowbear가 설명했듯이 checker변수를 비트 배열로 사용하려고합니다 . 예를 들어 접근 해 봅시다.

의 말을하자 str equals "test"

  • 첫 패스 (i = t)

검사기 == 0 (00000000000000000000000000000000)

In ASCII, val = str.charAt(i) - 'a' = 116 - 97 = 19
What about 1 << val ?
1          == 00000000000000000000000000000001
1 << 19    == 00000000000010000000000000000000
checker |= (1 << val) means checker = checker | (1 << val)
so checker = 00000000000000000000000000000000 | 00000000000010000000000000000000
checker == 524288 (00000000000010000000000000000000)
  • 두 번째 패스 (i = e)

검사기 == 524288 (00000000000010000000000000000000)

val = 101 - 97 = 4
1          == 00000000000000000000000000000001
1 << 4     == 00000000000000000000000000010000
checker |= (1 << val) 
so checker = 00000000000010000000000000000000 | 00000000000000000000000000010000
checker == 524304 (00000000000010000000000000010000)

조건을 통해 특정 문자에 대해 체커에서 이미 설정된 비트를 찾을 때까지

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

그것이 도움이되기를 바랍니다.


2
나머지 IMO보다 훨씬 더 나은 설명이지만 여전히 얻지 못하는 것은 checker = 00000000000010000000000000000000 | 00000000000000000000000000010000은 비트 단위 OR 연산자가 아닙니다. 그 이후로 하나의 가치를 선택하지 않겠습니까? 왜 비트를 사용하고 설정합니까?
CodeCrack

@CodeCrack은 비트 OR이라고 말했습니다. 비트 배열 레벨이 아닌 비트 레벨에서 비교합니다. 참고 : int는 비트 배열입니다
MusicMan

7

위에 제공된 훌륭한 답변이 몇 가지 있습니다. 그래서 나는 이미 말한 모든 것을 반복하고 싶지 않습니다. 그러나 방금 동일한 프로그램을 통해 작업하고 몇 가지 질문을했지만 위의 프로그램에 도움이되는 몇 가지 사항을 추가하고 싶었지만 시간을 보낸 후이 프로그램에 대해 더 명확하게 설명했습니다.

우선 "체커"는 문자가 반복되는지 확인하기 위해 문자열에서 이미 순회 한 문자를 추적하는 데 사용됩니다.

이제 "checker"는 int 데이터 형식이므로 32 비트 또는 4 바이트 (플랫폼에 따라 다름) 만 가질 수 있으므로이 프로그램은 32 자 범위의 문자 집합에 대해서만 올바르게 작동 할 수 있습니다. 그렇기 때문에이 프로그램은 각 문자에서 'a'를 빼서이 프로그램을 소문자로만 실행합니다. 그러나 소문자와 대문자를 혼합하면 작동하지 않습니다.

그건 그렇고, 각 문자에서 'a'를 빼지 않으면 (아래 문장 참조)이 프로그램은 대문자가있는 문자열 또는 소문자 만있는 문자열에 대해서만 올바르게 작동합니다. 따라서 위 프로그램의 범위는 소문자에서 대문자로 증가하지만 함께 혼합 할 수는 없습니다.

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

그러나 대문자, 소문자, 숫자 또는 특수 문자를 걱정하지 않고 모든 ASCII 문자에서 작동하는 Bitwise Operation을 사용하여 일반 프로그램을 작성하고 싶었습니다. 이렇게하려면 "체커"가 256자를 저장할 수있을 정도로 커야합니다 (ASCII 문자 세트 크기). 그러나 Java의 int는 32 비트 만 저장할 수 있으므로 작동하지 않습니다. 따라서 아래 프로그램에서 JDK에서 사용 가능한 BitSet 클래스를 사용하고 있습니다.이 클래스는 BitSet 객체를 인스턴스화하는 동안 모든 사용자 정의 크기를 전달할 수 있습니다.

다음은 비트 단위 연산자를 사용하여 작성된 위의 프로그램과 동일한 작업을 수행하는 프로그램이지만 ASCII 문자 집합의 모든 문자가 포함 된 문자열에 대해 작동합니다.

public static boolean isUniqueStringUsingBitVectorClass(String s) {

    final int ASCII_CHARACTER_SET_SIZE = 256;

    final BitSet tracker = new BitSet(ASCII_CHARACTER_SET_SIZE);

    // if more than  256 ASCII characters then there can't be unique characters
    if(s.length() > 256) {
        return false;
    }

    //this will be used to keep the location of each character in String
    final BitSet charBitLocation = new BitSet(ASCII_CHARACTER_SET_SIZE);

    for(int i = 0; i < s.length(); i++) {

        int charVal = s.charAt(i);
        charBitLocation.set(charVal); //set the char location in BitSet

        //check if tracker has already bit set with the bit present in charBitLocation
        if(tracker.intersects(charBitLocation)) {
            return false;
        }

        //set the tracker with new bit from charBitLocation
        tracker.or(charBitLocation);

        charBitLocation.clear(); //clear charBitLocation to store bit for character in the next iteration of the loop

    }

    return true;

}

1
이 솔루션을 찾고 있었지만 두 개의 BitSet 변수가 필요하지 않습니다. 추적기로 충분합니다. 루프 코드 용으로 업데이트 : for(int i = 0; i < s.length(); i++) { int charVal = s.charAt(i); if(tracker.get(charVal)) { return false; } tracker.set(charVal); }
zambro

7

위의 Ivan의 대답을 읽으면 실제로 도움이되었지만 다소 다르게 표현할 수는 있습니다.

<<에서이 (1 << val)비트 쉬프트 연산자이다. 그것은 1(바이너리에서로 표시되고 000000001원하는만큼의 0을 메모리에 의해 할당 됨) val공백으로 왼쪽으로 이동 합니다. 우리는 az 만 가정하고 a매번 빼기 때문에 각 문자의 값은 0-25입니다.이 숫자는 checker정수의 부울 표현의 오른쪽에서 해당 문자의 인덱스 가됩니다 . 시간을 1왼쪽으로 이동 하기 때문 checker val입니다.

각 점검이 끝나면 |=운영자 가 보입니다 . 이 인덱스에서 두 피연산자에 존재 하는 경우 모든이를 0의로 대체하여 두 개의 이진수를 병합합니다 . 여기에 있음은이 곳마다 그 수단 에 존재 , 로 복사됩니다 의 동안 모두 의 기존 1 개의은 유지됩니다.111(1 << val)1checkerchecker

짐작할 수 있듯이 1여기 의 함수는 true에 대한 부울 플래그입니다. 문자열에 문자가 이미 표시되어 있는지 확인하면 checker이 시점에서 이미 표현 된 1문자 색인의 기본 부울 플래그 ( 값) 배열과 기본 배열이 무엇인지 비교합니다. 1현재 문자의 색인에 플래그 가있는 부울 값

&오퍼레이터는이 검사를 달성한다. 받는 유사 |=&연산자는 이상 복사합니다 1 에만 두 피연산자가있는 경우 1해당 인덱스에서. 따라서 기본적으로 이미 checker표시된 플래그 만 (1 << val)복사됩니다. 이 경우, 현재 문자가 이미 표시된 경우에만 1결과의 어디에나 선물 이 표시됩니다 checker & (1 << val). 그리고 1그 연산의 결과에 a 가 있으면 반환 된 부울의 값은 > 0이고, 메소드는 false를 반환합니다.

이것은 비트 벡터가 비트 배열 이라고도하는 이유 입니다. 배열 데이터 유형이 아니더라도 부울 플래그를 저장하기 위해 배열을 사용하는 방식과 유사하게 사용할 수 있습니다.


1
매우 도움이, 당신의 자바 정보 뿌리 주셔서 감사합니다.
Bachiri Taoufiq Abderrahman

4

간단한 설명 (아래 JS 코드 포함)

  • 머신 코드 당 정수 변수는 32 비트 배열입니다.
  • 모든 비트 현명한 작업은 32-bit
  • 그것들은 OS / CPU 아키텍처 또는 DEC64JS 와 같은 선택된 언어의 숫자 체계 를 무시합니다.
  • 이 중복 찾는 방법과 유사한 크기 (32)의 배열에 문자를 저장 , 우리가 설정 0th우리가 발견하는 경우 인덱스를 a, 문자열에 1st대한 b& 등등.
  • 문자열의 중복 문자는 해당 비트를 점유하거나이 경우 1로 설정합니다.
  • Ivan은 이미 설명했다 :이 이전 답변에서이 인덱스 계산이 어떻게 작동 하는가 .

작업 요약 :

  • 문자 & 사이의 AND 연산 수행checkerindex
  • 내부적으로 둘 다 Int-32-Arrays
  • 이 둘 사이의 비트 단위 연산입니다.
  • if작업의 출력을 확인하십시오1
  • 만약 output == 1
    • checker변수를 갖는 특정 인덱스 번째 비트 두 배열의 집합
    • 따라서 중복입니다.
  • 만약 output == 0
    • 이 캐릭터는 지금까지 발견되지 않았습니다
    • & 의 문자 사이에 OR 연산을 수행하십시오.checkerindex
    • 이에 의해, 인덱스 비트를 다음으로 갱신한다 1
    • 출력을 할당 checker

가정 :

  • 우리는 모든 소문자를 얻을 것이라고 가정했습니다
  • 그리고 그 크기 32면 충분합니다
  • 따라서, 우리는에서 우리의 인덱스 계산을 시작 기준으로 96 고려 점 아스키 에 대한 코드 aIS를97

아래는 JavaScript 소스 코드입니다.

function checkIfUniqueChars (str) {

    var checker = 0; // 32 or 64 bit integer variable 

    for (var i = 0; i< str.length; i++) {
        var index = str[i].charCodeAt(0) - 96;
        var bitRepresentationOfIndex = 1 << index;

        if ( (checker & bitRepresentationOfIndex) > 1) {
            console.log(str, false);
            return false;
        } else {
            checker = (checker | bitRepresentationOfIndex);
        }
    }
    console.log(str, true);
    return true;
}

checkIfUniqueChars("abcdefghi");  // true
checkIfUniqueChars("aabcdefghi"); // false
checkIfUniqueChars("abbcdefghi"); // false
checkIfUniqueChars("abcdefghii"); // false
checkIfUniqueChars("abcdefghii"); // false

JS에서 64 비트로 인 정수에도 불구하고, 비트 단위 조작은 항상 32 비트에 수행된다.

예 : 문자열이 aa다음과 같은 경우 :

// checker is intialized to 32-bit-Int(0)
// therefore, checker is
checker= 00000000000000000000000000000000

나는 = 0

str[0] is 'a'
str[i].charCodeAt(0) - 96 = 1

checker 'AND' 32-bit-Int(1) = 00000000000000000000000000000000
Boolean(0) == false

// So, we go for the '`OR`' operation.

checker = checker OR 32-bit-Int(1)
checker = 00000000000000000000000000000001

나는 = 1

str[1] is 'a'
str[i].charCodeAt(0) - 96 = 1

checker= 00000000000000000000000000000001
a      = 00000000000000000000000000000001

checker 'AND' 32-bit-Int(1) = 00000000000000000000000000000001
Boolean(1) == true
// We've our duplicate now

3

코드를 한 줄씩 세분화합니다.

정수 검사기 = 0; 중복 값을 찾는 데 도움이되는 검사기를 시작합니다.

int val = str.charAt (i)- 'a'; 문자열의 'i'위치에서 문자의 ASCII 값을 가져와 ASCII 값 'a'로 뺍니다. 문자열이 더 낮은 문자 일 뿐이라고 가정하므로 문자 수는 26 개로 제한됩니다. 히스, 'val'값은 항상> = 0입니다.

if ((검사기 & (1 << val))> 0) false를 반환합니다.

검사기 | = (1 << val);

이제 까다로운 부분입니다. 문자열 "abcda"가 포함 된 예를 살펴 보겠습니다. 이상적으로 false를 반환해야합니다.

루프 반복 1의 경우 :

검사기 : 00000000000000000000000000000000

발 : 97-97 = 0

1 << 0 : 00000000000000000000000000000001

검사기 & (1 << val) : 00000000000000000000000000000000이> 0이 아님

따라서 검사기 : 00000000000000000000000000000001

루프 반복 2의 경우 :

검사기 : 00000000000000000000000000000001

발 : 98-97 = 1

1 << 0 : 00000000000000000000000000000010

검사기 & (1 << val) : 00000000000000000000000000000000이> 0이 아님

따라서 검사기 : 00000000000000000000000000000011

루프 반복 3의 경우 :

검사기 : 00000000000000000000000000000011

발 : 99-97 = 0

1 << 0 : 00000000000000000000000000000100

검사기 & (1 << val) : 00000000000000000000000000000000이> 0이 아님

따라서 검사기 : 00000000000000000000000000000111

루프 반복 4의 경우 :

검사기 : 00000000000000000000000000000111

발 : 100-97 = 0

1 << 0 : 00000000000000000000000000001000

검사기 & (1 << val) : 00000000000000000000000000000000이> 0이 아님

따라서 검사기 : 00000000000000000000000000001111

루프 반복 5의 경우 :

검사기 : 00000000000000000000000000001111

발 : 97-97 = 0

1 << 0 : 00000000000000000000000000000001

검사기 & (1 << val) : 00000000000000000000000000000001은> 0

따라서 거짓을 반환합니다.


val : 99-97 = 0 val : 99-97 = 2이고 val : 100-97 = 0이어야합니다 3
Brosef

2
public static void main (String[] args)
{
    //In order to understand this algorithm, it is necessary to understand the following:

    //int checker = 0;
    //Here we are using the primitive int almost like an array of size 32 where the only values can be 1 or 0
    //Since in Java, we have 4 bytes per int, 8 bits per byte, we have a total of 4x8=32 bits to work with

    //int val = str.charAt(i) - 'a';
    //In order to understand what is going on here, we must realize that all characters have a numeric value
    for (int i = 0; i < 256; i++)
    {
        char val = (char)i;
        System.out.print(val);
    }

    //The output is something like:
    //             !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
    //There seems to be ~15 leading spaces that do not copy paste well, so I had to use real spaces instead

    //To only print the characters from 'a' on forward:
    System.out.println();
    System.out.println();

    for (int i=0; i < 256; i++)
    {
        char val = (char)i;
        //char val2 = val + 'a'; //incompatible types. required: char found: int
        int val2 = val + 'a';  //shift to the 'a', we must use an int here otherwise the compiler will complain
        char val3 = (char)val2;  //convert back to char. there should be a more elegant way of doing this.
        System.out.print(val3);
    }

    //Notice how the following does not work:
    System.out.println();
    System.out.println();

    for (int i=0; i < 256; i++)
    {
        char val = (char)i;
        int val2 = val - 'a';
        char val3 = (char)val2;
        System.out.print(val3);
    }
    //I'm not sure why this spills out into 2 lines:
    //EDIT I cant seem to copy this into stackoverflow!

    System.out.println();
    System.out.println();

    //So back to our original algorithm:
    //int val = str.charAt(i) - 'a';
    //We convert the i'th character of the String to a character, and shift it to the right, since adding shifts to the right and subtracting shifts to the left it seems

    //if ((checker & (1 << val)) > 0) return false;
    //This line is quite a mouthful, lets break it down:
    System.out.println(0<<0);
    //00000000000000000000000000000000
    System.out.println(0<<1);
    //00000000000000000000000000000000
    System.out.println(0<<2);
    //00000000000000000000000000000000
    System.out.println(0<<3);
    //00000000000000000000000000000000
    System.out.println(1<<0);
    //00000000000000000000000000000001
    System.out.println(1<<1);
    //00000000000000000000000000000010 == 2
    System.out.println(1<<2);
    //00000000000000000000000000000100 == 4
    System.out.println(1<<3);
    //00000000000000000000000000001000 == 8
    System.out.println(2<<0);
    //00000000000000000000000000000010 == 2
    System.out.println(2<<1);
    //00000000000000000000000000000100 == 4
    System.out.println(2<<2);
    // == 8
    System.out.println(2<<3);
    // == 16
    System.out.println("3<<0 == "+(3<<0));
    // != 4 why 3???
    System.out.println(3<<1);
    //00000000000000000000000000000011 == 3
    //shift left by 1
    //00000000000000000000000000000110 == 6
    System.out.println(3<<2);
    //00000000000000000000000000000011 == 3
    //shift left by 2
    //00000000000000000000000000001100 == 12
    System.out.println(3<<3);
    // 24

    //It seems that the -  'a' is not necessary
    //Back to if ((checker & (1 << val)) > 0) return false;
    //(1 << val means we simply shift 1 by the numeric representation of the current character
    //the bitwise & works as such:
    System.out.println();
    System.out.println();
    System.out.println(0&0);    //0
    System.out.println(0&1);       //0
    System.out.println(0&2);          //0
    System.out.println();
    System.out.println();
    System.out.println(1&0);    //0
    System.out.println(1&1);       //1
    System.out.println(1&2);          //0
    System.out.println(1&3);             //1
    System.out.println();
    System.out.println();
    System.out.println(2&0);    //0
    System.out.println(2&1);       //0   0010 & 0001 == 0000 = 0
    System.out.println(2&2);          //2  0010 & 0010 == 2
    System.out.println(2&3);             //2  0010 & 0011 = 0010 == 2
    System.out.println();
    System.out.println();
    System.out.println(3&0);    //0    0011 & 0000 == 0
    System.out.println(3&1);       //1  0011 & 0001 == 0001 == 1
    System.out.println(3&2);          //2  0011 & 0010 == 0010 == 2, 0&1 = 0 1&1 = 1
    System.out.println(3&3);             //3 why?? 3 == 0011 & 0011 == 3???
    System.out.println(9&11);   // should be... 1001 & 1011 == 1001 == 8+1 == 9?? yay!

    //so when we do (1 << val), we take 0001 and shift it by say, 97 for 'a', since any 'a' is also 97

    //why is it that the result of bitwise & is > 0 means its a dupe?
    //lets see..

    //0011 & 0011 is 0011 means its a dupe
    //0000 & 0011 is 0000 means no dupe
    //0010 & 0001 is 0011 means its no dupe
    //hmm
    //only when it is all 0000 means its no dupe

    //so moving on:
    //checker |= (1 << val)
    //the |= needs exploring:

    int x = 0;
    int y = 1;
    int z = 2;
    int a = 3;
    int b = 4;
    System.out.println("x|=1 "+(x|=1));  //1
    System.out.println(x|=1);     //1
    System.out.println(x|=1);      //1
    System.out.println(x|=1);       //1
    System.out.println(x|=1);       //1
    System.out.println(y|=1); // 0001 |= 0001 == ?? 1????
    System.out.println(y|=2); // ??? == 3 why??? 0001 |= 0010 == 3... hmm
    System.out.println(y);  //should be 3?? 
    System.out.println(y|=1); //already 3 so... 0011 |= 0001... maybe 0011 again? 3?
    System.out.println(y|=2); //0011 |= 0010..... hmm maybe.. 0011??? still 3? yup!
    System.out.println(y|=3); //0011 |= 0011, still 3
    System.out.println(y|=4);  //0011 |= 0100.. should be... 0111? so... 11? no its 7
    System.out.println(y|=5);  //so we're at 7 which is 0111, 0111 |= 0101 means 0111 still 7
    System.out.println(b|=9); //so 0100 |= 1001 is... seems like xor?? or just or i think, just or... so its 1101 so its 13? YAY!

    //so the |= is just a bitwise OR!
}

public static boolean isUniqueChars(String str) {
    int checker = 0;
    for (int i = 0; i < str.length(); ++i) {
        int val = str.charAt(i) - 'a';  //the - 'a' is just smoke and mirrors! not necessary!
        if ((checker & (1 << val)) > 0) return false;
        checker |= (1 << val);
    }
    return true;
}

public static boolean is_unique(String input)
{
    int using_int_as_32_flags = 0;
    for (int i=0; i < input.length(); i++)
    {
        int numeric_representation_of_char_at_i = input.charAt(i);
        int using_0001_and_shifting_it_by_the_numeric_representation = 1 << numeric_representation_of_char_at_i; //here we shift the bitwise representation of 1 by the numeric val of the character
        int result_of_bitwise_and = using_int_as_32_flags & using_0001_and_shifting_it_by_the_numeric_representation;
        boolean already_bit_flagged = result_of_bitwise_and > 0;              //needs clarification why is it that the result of bitwise & is > 0 means its a dupe?
        if (already_bit_flagged)
            return false;
        using_int_as_32_flags |= using_0001_and_shifting_it_by_the_numeric_representation;
    }
    return true;
}

0

이전 게시물은 코드 블록의 기능을 잘 설명하고 있으며 BitSet java Data 구조를 사용하여 간단한 솔루션을 추가하고 싶습니다.

private static String isUniqueCharsUsingBitSet(String string) {
  BitSet bitSet =new BitSet();
    for (int i = 0; i < string.length(); ++i) {
        int val = string.charAt(i);
        if(bitSet.get(val)) return "NO";
        bitSet.set(val);
    }
  return "YES";
}

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

Javascript를 사용하여 이해 한 방식. 입력 가정var inputChar = "abca"; //find if inputChar has all unique characters

시작하자

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

위의 행은 inputChar에서 첫 번째 문자의 이진 값인 ascii에서 a , a = 97 을 찾은 다음 97을 이진으로 변환하면 1100001됩니다 .

자바 스크립트 예 : "a".charCodeAt().toString(2) 1100001 반환

checker = 0 // 이진 32 비트 표현 = 0000000000000000000000000

checker = 1100001 | checker; // 체커는 1100001이됩니다 (32 비트 표현에서는 000000000 ..... 00001100001이됩니다)

하지만 비트 마스크 ( int checker)가 하나의 비트 만 설정하기를 원하지만 검사기는 1100001입니다.

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

이제 위의 코드가 편리합니다. 난 그냥 97 항상 빼기 (ASCII val of a)

val = 0; // 97 - 97  Which is  a - a
val = 1; // 98 - 97 Which is b - a
val = 1;  // 99 - 97 Which is c - a

사용할 수 있습니다 valresetted됩니다

5 번 줄과 6 번 줄은 @Ivan answer


0

누군가가 비트 벡터를 사용하여 문자열에서 고유 문자에 해당하는 kotlin을 찾고 있다면

fun isUnique(str: String): Boolean {
    var checker = 0
    for (i in str.indices) {
        val bit = str.get(i) - 'a'
        if (checker.and(1 shl bit) > 0) return false
        checker = checker.or(1 shl bit)
    }
    return true
}

참조 : https://www.programiz.com/kotlin-programming/bitwise

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.