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


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
このコードは、「The Cracking The Code Interview
Dejell」の

2
これをテストしましたか?それを0に設定し、左シフト、それはまだ0でそれを維持するだので、それが重複「」文字を検出するために失敗するように思える
Rizの

3
ソリューションは小文字のa〜zに使用されることに注意してください。これは、26文字の重複を見つけるために使用していることを意味します。したがって、32ビットを取るintをここで使用できます。範囲が大きかった場合、ソリューションは機能しません。
a3.14_Infinity、2015

1
人々が間違っているのは、左シフト演算子の構文と混同していることです。x(= str.charAt(i)-'a')によって左に移動されるのは1であり、xのビットが1桁左にシフトされるわけではありません。
nanosoft

回答:


100

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

またint、ビットを使用した操作は非常に低レベルであり、CPUでそのまま実行できるため、おそらくもう少し速いかもしれません。BitVectorを使用すると、暗号化されたコードを少し減らすことができ、さらに多くのフラグを格納できます。

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


JavaにはBitVectorクラスがありますか?ドキュメントが見つかりませんでした!
デジェル2013

サイズは32ビットの固定サイズです。つまり、32文字の一意のみをテストできるということですか?私は、この関数が "abcdefgZZ"が偽であることをテストできることをテストしましたが、 "abcdefg @@"は真を返します。
tli2020 2014年

1
ここでGoogleが私を導きました。@Dejel使用できるJavaデータ構造は、docs.oracle.com/javase/7/docs/api/java/ util / BitSet.htmlです。うまくいけば、これがintertubesを通って旅行している誰かを助けるでしょう。
nattyddubbs 2014年

@nattyddubbsが、おかげで、私はこれと答えには、いくつかの他のリンクを追加しました
Snowbear

222

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

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

この演算子| =は、左側にオペランドをとり、orと右側のオペランドを組み合わせたもので、この演算子は、その左側と右側の両方のオペランドのビットです。

したがって、ここにあるのは、チェッカーがchecker |= (1 << val)対応するビットがtrueに設定されている文字の指定されたバイナリ値とチェッカーがor(d )されるたびに、32ビットの2進数で保存されるハッシュテーブルです。文字の値はチェッカー(checker & (1 << val)) > 0)でand'dされます。0より大きい場合、重複していることがわかります。trueに設定された2つの同一のビットがtrueまたは「1」を返すためです。

26のバイナリの場所があり、それぞれが小文字に対応しています-著者は文字列に小文字のみが含まれていると想定していました-これは、使用できる場所があと6つ(32ビット整数)だけ残っているためです。衝突する

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

今、それは重複を宣言します


@ ivan-tichyこれをテストしましたか?それを0に設定し、左シフト、それはまだ0でそれを維持するだので、それが重複「」文字を検出するために失敗するように思える
Rizの

1
@Rizいいえ、常に「1」で始まり、アルゴリズムは文字に基づいて1をシフトします。したがって、文字「a」が1回来ると、1になります(.... 000001)。
テイラーハリデー2014

2
@Ivan Man、私は同じことを考えていました。選ばれた答えでさえ演算子について説明しませんでした。詳細情報ありがとうございます。
WowBow 2015年

上記の一意のチェックは、並べ替えられた文字セット(abcd ... z)でのみ機能すると思いますか?(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 16

明確な説明
..ありがとう

素晴らしい説明。わかりやすい。ありがとう
Anil Kumar

それが最高です
ウラジミールナボコフ

これがコメントが発明されたまさにその理由です。
Mr. Suryaa Jha

30

私はまた、あなたの例は本「コードインタビューのクラッキング」から来ていると思います。私の答えはこのコンテキストに関連しています。

このアルゴリズムを使用して問題を解決するには、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

それが役に立てば幸い


2
残りのIMOよりもはるかに優れた説明ですが、私がまだ取得できないことの1つは、checker = 00000000000010000000000000000000です。00000000000000000000000000010000は、ビット単位の| = OR演算子ではありません。それ以来、どちらか一方の値を選択しませんか?なぜそれを使用して設定し、両方のビットを使用するのですか?
CodeCrack、2015年

@CodeCrackは、ビット単位のORだと言っていました。ビットアレイレベルではなく、ビットレベルで比較します。注:intはビット配列です
MusicMan

7

上記ですでに提供されている優れた回答がいくつかあります。だから私はすでに言ったことすべてを繰り返したくありません。しかし、上記のプログラムを支援するためにいくつかのことを追加したいと思いました。同じプログラムを処理したばかりで、いくつかの質問がありましたが、少し時間をかけて、このプログラムについてより明確になりました。

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

現在「チェッカー」は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
このソリューションを探していましたが、2つの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

上記のイヴァンの答えを読んだことは本当に助かりました。

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

各チェックの最後に、|=オペレーターが表示されます。これにより、2つの2進数がマージされ、そのインデックスのいずれかのオペランドにa が存在する場合、すべて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
非常に参考になりました。Java情報をお寄せいただきありがとうございます。
Bachiri Taoufiq Abderrahman、2018

4

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

  • マシンコードごとの整数変数は32ビット配列です
  • すべてのビット単位の操作は 32-bit
  • それらは、OS / CPUアーキテクチャやDEC64、JS などの言語の選択された数体系に依存しません。
  • アプローチを見つけるこの重複はに似ているサイズ32の配列内の文字を格納し、我々が設定され、0th私たちが発見した場合、インデックスをa、文字列に1stするためにb&そうで。
  • 文字列内の重複する文字は、対応するビットが占有されるか、この場合は1に設定されます。
  • Ivanはすでに説明しています:この前の回答でこのインデックス計算はどのように機能します

オペレーションの概要:

  • 文字の&の間でAND演算を実行しますcheckerindex
  • 内部的には両方とも Int-32-Arrays
  • これは、これら2の間のビット単位の演算です。
  • if操作の出力を確認しました1
  • もし output == 1
    • checker変数が有している特定のインデックス番目のビットの両方の配列にセットし
    • したがって、それは複製です。
  • もし output == 0
    • このキャラクターは今のところ見つかりません
    • 文字の&の間でOR演算を実行しますcheckerindex
    • これにより、インデックス番目のビットを 1
    • 出力をに割り当てます checker

仮定:

  • すべての小文字が取得されると想定しています
  • そして、そのサイズ32で十分です
  • したがって、我々は、Googleのインデックスからカウントを開始し、参照として96考慮ポイントアスキーのコードaです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 = 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

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

3

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

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

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

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

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

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

ループ反復1の場合:

チェッカー:00000000000000000000000000000000

値:97-97 = 0

1 << 0:00000000000000000000000000000001

チェッカー&(1 << val):00000000000000000000000000000000 is not> 0

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

ループ反復2の場合:

チェッカー:00000000000000000000000000000001

値:98-97 = 1

1 << 0:00000000000000000000000000000010

チェッカー&(1 << val):00000000000000000000000000000000 is not> 0

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

ループ反復3の場合:

チェッカー:00000000000000000000000000000011

val:99-97 = 0

1 << 0:00000000000000000000000000000100

チェッカー&(1 << val):00000000000000000000000000000000 is not> 0

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

ループ反復4の場合:

チェッカー:00000000000000000000000000000111

値:100-97 = 0

1 << 0:00000000000000000000000000001000

チェッカー&(1 << val):00000000000000000000000000000000 is not> 0

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

ループ反復5の場合:

チェッカー:00000000000000000000000000001111

値:97-97 = 0

1 << 0:00000000000000000000000000000001

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

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


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データ構造を使用して簡単なソリューションを追加したいと思います。

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でaa = 97であり、97をバイナリに変換すると1100001になり ます

JavaScript Egの場合:"a".charCodeAt().toString(2) 1100001を返します

checker = 0 //バイナリ32ビット表現= 0000000000000000000000000

checker = 1100001 | checker; //チェッカーは1100001になります(32ビット表現では000000000 ..... 00001100001になります)

しかし、ビットマスク(int checker)に1ビットのみを設定したいのですが、チェッカーは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

valリセットされたものを使ってみましょう

5行目と6行目はよく説明されています@Ivanの回答


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.