web-dev-qa-db-ja.com

すべての文字が一意かどうかを判断するためのビットベクトルの使用について説明する

私はビットベクトルがこれを行うためにどのように機能するかについて混乱しています(ビットベクトルにあまり慣れていません)。以下にコードを示します。誰かが私にこれを説明してもらえますか?

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は何をしていますか?

122
user1136342

_int checker_は、ビットのストレージとしてここで使用されます。整数値のすべてのビットはフラグとして扱うことができるため、最終的にintはビット(フラグ)の配列になります。コードの各ビットは、ビットのインデックスを持つ文字が文字列で見つかったかどうかを示します。同じ理由で、intの代わりにビットベクトルを使用することもできます。それらの間には2つの違いがあります。

  • サイズ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

また、ビットを使用した操作は非常に低レベルであり、CPUによってそのまま実行できるため、おそらくintは少し高速になる可能性があります。 BitVectorでは、暗号化コードを少し少なくすることができ、さらに多くのフラグを保存できます。

将来の参照用:ビットベクトルは、bitSetまたはbitArrayとも呼ばれます。さまざまな言語/プラットフォームのこのデータ構造へのリンクを次に示します。

88
Snowbear

私が読んでいるのと同じ本からこのコードを手に入れたという疑いがあります...ここのコード自体は、演算子-| =、&、および<<通常使用されない私たち素人-著者は、プロセスの説明や、ここに含まれる実際のメカニックスの説明に余分な時間を費やすことはありませんでした。私はこのスレッドに関する最初の回答に最初は満足していましたが、抽象レベルでのみでした。もっと具体的な説明が必要だと感じたので、私はそれに戻りました。1つが欠けているといつも不安な気持ちになります。

この演算子<<は左ビット単位のシフターであり、その数値またはオペランドのバイナリ表現を取り、バイナリのみの10進数のように右側のオペランドまたは数値で指定された多くの場所にシフトします。 10を基点としてではなく、多くの場所を上に移動するときに2を基数で乗算しているため、右側の数字は指数であり、左側の数字は2の基数倍です。

この演算子| =は、左側のオペランドを取り、ORを右側のオペランドと取ります。そして、この演算子は、両方のオペランドのビットを左右に取ります。

したがって、ここにあるのは、チェッカーが対応するビットの文字の指定されたバイナリ値で(checker |= (1 << val))を取得するたびに32ビットのバイナリ番号に格納されているハッシュテーブルですtrueに設定します。文字の値は、チェッカー(checker & (1 << val)) > 0)でand'dされます。0より大きい場合、2つの同一のビットがtrueに設定され、一緒に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  

文字列「azy」

checker= 00000010000000000000000000000001    
y      = 00000001000000000000000000000000 

checker and y=0 no dupes condition 

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

文字列「azya」

checker= 00000011000000000000000000000001
a      = 00000000000000000000000000000001

a and checker=1 we have a dupe

現在、duplicateを宣言しています

187
Ivan Tichy

また、あなたの例は本から出てくると思います 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)
  • 2回目のパス(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

それが役に立てば幸い

28
Alex Bretet

これらの答えはすべてこれがどのように機能するかを説明していると思いますが、いくつかの変数の名前を変更し、他の変数を追加し、コメントを追加することで、私がそれをどのように見たのかについて自分の意見を述べたいと感じました:

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;
}
24
Miguel Durazo

上記のIvanの答えを読むことは本当に助けになりましたが、言い方は多少異なります。

<<(1 << val)はビットシフト演算子です。 1(バイナリで000000001として表され、好きなだけ多くの先行ゼロがメモリによって割り当てられる)を取り、それをvalスペースだけ左にシフトします。 azのみを想定し、毎回aを減算するので、各文字の値は0-25になります。これは、1を左に移動するため、checker整数のブール表現の右からその文字のインデックスになりますcheckerval回。

各チェックの最後に、|=演算子があります。これは2つの2進数をマージし、そのインデックスのいずれかのオペランドに0が存在する場合、すべての11に置き換えます。ここで、それは、1(1 << val)のどこに存在する場合でも、その1checkerにコピーされ、checkerの既存の1はすべて保持されることを意味します。

おそらく推測できるように、ここで1はtrueのブールフラグとして機能します。文字が既に文字列で表されているかどうかを確認するとき、checkerを比較します。この変数は、この時点で、すでに表されている文字のインデックスにあるブールフラグ(1値)の配列です。現在の文字のインデックスに1フラグを付けたブール値の配列。

&演算子はこのチェックを実行します。 |=と同様に、&演算子は、両方のオペランドのインデックスに1がある場合、1onlyをコピーします。そのため、本質的には(1 << val)でも表されるcheckerに既に存在するフラグのみがコピーされます。この場合、現在の文字がすでに表現されている場合にのみ、checker & (1 << val)の結果のどこかに1が存在することを意味します。また、1がその操作の結果のどこかに存在する場合、返されるブール値の値は> 0であり、メソッドはfalseを返します。

これが、ビットベクトルがビット配列とも呼ばれる理由です。配列データ型ではありませんが、ブールフラグを格納するために配列を使用する方法と同様に使用できるためです。

6
Matthew Hinea

上記で既に提供された優れた回答がいくつかあります。だから、私はすでに言ったことすべてを繰り返したくありません。しかし、上記のプログラムに役立つものをいくつか追加したいと思ったのは、同じプログラムを試したばかりで、いくつか質問がありましたが、しばらく時間を費やした後、このプログラムについてより明確になったからです。

まず、「チェッカー」を使用して、文字列で既にトラバースされている文字を追跡し、繰り返される文字があるかどうかを確認します。

現在、「チェッカー」は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;

}
5

簡単な説明(JSコードを使用)

  • マシンコードごとの整数変数は2ビット配列
  • ビット単位の演算はすべて32-bitです
  • OS/CPUアーキテクチャまたは言語の選択された番号システムにとらわれません。 JSの場合はDEC64
  • この重複発見のアプローチはサイズ32の配列に文字を保存するに似ています。ここで、文字列にaが見つかった場合は0thインデックスを設定し、bには1stなどを設定します。
  • 文字列内の重複する文字は、対応するビットが占有されるか、この場合は1に設定されます。
  • Ivanはすでに説明しました:この前の回答でのこのインデックス計算の仕組み

操作の概要:

  • [〜#〜] and [〜#〜]キャラクターのcheckerindexの間の操作を実行します
  • 内部的には両方ともInt-32-Arrays
  • これら2の間のビット単位の操作です。
  • ifを確認してください。操作の出力は1でした
  • if output == 1
    • checker変数には、両方の配列でその特定のインデックス番目のビットが設定されています
    • したがって、それは複製です。
  • if output == 0
    • これまでにこのキャラクターは見つかりませんでした
    • キャラクターのcheckerindexの間で[〜#〜] or [〜#〜]操作を実行する
    • これにより、インデックスのビットを1に更新します
    • 出力をcheckerに割り当てます

仮定:

  • 小文字をすべて取得すると仮定しました
  • そして、そのサイズ32で十分です
  • したがって、インデックスのカウントは96 as referenceポイントを考慮して開始しましたasciiaのコードは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

i =

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

i = 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
4
DDM

コードを行ごとに分解してみましょう。

int checker = 0;重複する値を見つけるのに役立つチェッカーを開始しています。

int val = str.charAt(i)-'a';文字列の「i」番目の位置にある文字のASCII値ASCII 'a'の値を使用して減算します。文字列は下の文字のみであると仮定されているため、文字数は26に制限されます。常に> = 0になります。

if((checker&(1 << val))> 0)return false;

チェッカー(1 << val);)==

今、これはトリッキーな部分です。文字列「abcda」の例を考えてみましょう。これは理想的にはfalseを返すはずです。

For loop iteration 1:

チェッカー:00000000000000000000000000000000

val:97-97 = 0

1 << 0:00000000000000000000000000000001

チェッカー&(1 << val):00000000000000000000000000000000は> 0ではありません

したがって、チェッカー:00000000000000000000000000000001

For loop iteration 2:

チェッカー:00000000000000000000000000000001

val:98-97 = 1

1 << 0:00000000000000000000000000000010

チェッカー&(1 << val):00000000000000000000000000000000は> 0ではありません

したがって、チェッカー:00000000000000000000000000000011

For loop iteration 3:

チェッカー:00000000000000000000000000000011

val:99-97 = 0

1 << 0:00000000000000000000000000000100

チェッカー&(1 << val):00000000000000000000000000000000は> 0ではありません

したがって、チェッカー:00000000000000000000000000000111

For loop iteration 4:

チェッカー:00000000000000000000000000000111

val:100-97 = 0

1 << 0:00000000000000000000000000001000

チェッカー&(1 << val):00000000000000000000000000000000は> 0ではありません

したがって、チェッカー:00000000000000000000000000001111

For loop iteration 5:

チェッカー:00000000000000000000000000001111

val:97-97 = 0

1 << 0:00000000000000000000000000000001

チェッカー&(1 << val):00000000000000000000000000000001は> 0

したがって、falseを返します。

3
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;
}
2
asdf1234

以前の投稿では、コードブロックが何を行うのかをよく説明しており、BitSet Java Data structureを使用して簡単なソリューションを追加したいと思います。

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";
}