Tüm karakterlerin benzersiz olup olmadığını belirlemek için bir bit vektörünün kullanımını açıklayın


150

Bir bit vektörünün bunu yapmak için nasıl çalışacağı konusunda kafam karıştı (bit vektörlerine çok aşina değil). İşte verilen kod. Birisi lütfen bu konuda bana yol gösterebilir mi?

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

Özellikle ne checkeryapıyor?


Java var ama C / C ++ benzer bir şey varsa bu benim için daha yararlı olacaktır.
user1136342

101
Bu kod Cracking The Code Interview
Dejell

2
bunu test ettin mi 0 olarak ayarlandığından ve sola kaydırdığından, yinelenen 'a' karakterlerini algılayamayacak gibi görünüyor.
Riz

3
Çözümün daha düşük karakterler için kullanıldığını unutmayın, yani 26 karakter için yineleme bulmak için kullanıyoruz. Yani, int alma 32 bit burada kullanılabilir. Aralık daha büyük olsaydı, çözüm işe yaramaz.
a3.14_Infinity

1
İnsanların hata yaptığı yerde, sol kaydırma operatörü sözdizimi ile karıştırmaktır - x (= str.charAt (i) - 'a') sola taşınan 1 x biti 1 sıra sola kaydırmaz.
nanosoft

Yanıtlar:


100

int checkerburada bitler için bir depolama alanı olarak kullanılır. Tam sayı değerindeki her bit bir bayrak olarak ele alınabilir, bu nedenle sonunda intbir bit dizisidir (bayrak). Kodunuzdaki her bit, bit dizinine sahip karakterin dizede bulunup bulunmadığını belirtir. Bit vektörünü aynı nedenle kullanabilirsiniz int. Aralarında iki fark vardır:

  • Boyut . intsabit boyutu vardır, genellikle 4 bayt, yani 8 * 4 = 32 bit (bayraklar). Bit vektörü genellikle farklı boyutta olabilir veya boyutu yapıcıda belirtmelisiniz.

  • API . Bit vektörleri ile kodu okumak daha kolay olacaktır, muhtemelen böyle bir şey:

    vector.SetFlag(4, true); // set flag at index 4 as true

    çünkü intdaha düşük seviye bit mantık koduna sahip olacaksınız:

    checker |= (1 << 5); // set flag at index 5 to true

Ayrıca int, bitlerle işlemler çok düşük seviyededir ve CPU tarafından olduğu gibi yürütülebilir çünkü muhtemelen biraz daha hızlı olabilir. BitVector bunun yerine biraz daha az şifreli kod yazmanıza izin verir ve ayrıca daha fazla bayrak saklayabilir.

İleride başvurmak üzere: bit vektörü bitSet veya bitArray olarak da bilinir. Farklı diller / platformlar için bu veri yapısına bağlantılar:


Java'nın bir BitVector sınıfı var mı? Herhangi bir belge bulamadım!
Dejell

Boyut, 32 bit olan sabit boyuta sahiptir. Bu sadece 32 karakterin benzersiz olduğunu test edebileceği anlamına mı geliyor? Bu işlev "abcdefgZZ" yanlış, ancak "abcdefg @@" test doğru olabilir test var.
tli2020

1
Google beni buraya getirdi. @Dejel Kullanabileceğiniz java veri yapısı: docs.oracle.com/javase/7/docs/api/java/util/BitSet.html . İnşallah bu tüpler arasında seyahat eden birine yardımcı olur.
nattyddubbs

@nattyddubbs, teşekkürler, cevaba bu ve diğer birkaç bağlantıyı
ekledim

223

Okuduğum aynı kitaptan bu kodu aldığınız bir sinsi şüphem var ... Kodun kendisi neredeyse normalde tarafından kullanılmayan operatörler - | =, &, ve << kadar şifreli değil us layman- yazar, süreci ya da buradaki gerçek mekaniğin ne olduğunu açıklamak için fazladan zaman ayırmaya zahmet etmedi. Başlangıçta bu konudaki önceki cevaptan memnun kaldım, ancak sadece soyut bir seviyede. Geri döndüm, çünkü daha somut bir açıklama olması gerektiğini hissettim - birinin eksikliği beni her zaman huzursuz bir his bırakır.

Bu operatör <<, o sayının veya işlenenin ikili temsilini alan ve yalnızca ondalık sayılardaki gibi ondalık sayılarda olduğu gibi sağdaki işlenen veya sayı tarafından belirtilen birçok yere kaydırır. Biz taban 2 ile çarpıyoruz-yukarı hareket ettiğimizde ancak 10 taban değil birçok yerde hareket ediyoruz, bu yüzden sağdaki sayı üstür ve soldaki sayı 2'nin bir katıdır.

Bu operatör | = soldaki işleneni alır ve veya sağdaki işlenene sahipse - ve bu - '&' ve her iki işlenenin sol ve sağ tarafındaki bitleri.

Yani burada sahip olduğumuz hash tablosu, her denetleyicinin checker |= (1 << val)karşılık gelen bit değeri true olarak ayarlanmış olan bir harfin belirtilen ikili değeri ile her aldığında (veya ) 32 bit ikili sayıda saklanmaktadır . Karakterin değeri checker ( checker & (1 << val)) > 0) ile olur ve ( 0'dan büyükse, bir dupomuz olduğunu biliyoruz - çünkü iki özdeş bit true olarak ayarlanmış ve birlikte true veya '1' 'döndürecektir.

Her biri bir küçük harfe karşılık gelen 26 ikili yer vardır-yazar dize sadece küçük harfler içerdiğini varsaymıştır - ve bunun nedeni sadece tüketmek için sadece 6 tane daha (32 bit tamsayıda) yer kalması ve çarpışmak

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

Yani, 'azya' giriş dizesi için, adım adım ilerlerken

'a' dizesi

a      =00000000000000000000000000000001
checker=00000000000000000000000000000000

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

a and checker=0 no dupes condition

dize 'az'

checker=00000000000000000000000000000001
z      =00000010000000000000000000000000

z and checker=0 no dupes 

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

dize 'azy'

checker= 00000010000000000000000000000001    
y      = 00000001000000000000000000000000 

checker and y=0 no dupes condition 

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

dize 'azya'

checker= 00000011000000000000000000000001
a      = 00000000000000000000000000000001

a and checker=1 we have a dupe

Şimdi, yinelenen


@ ivan-tichy bunu test ettiniz mi? 0 olarak ayarlandığından ve sola kaydırdığından, yinelenen 'a' karakterlerini algılayamayacak gibi görünüyor.
Riz

1
@Riz Hayır, her zaman '1' ile başlar, algoritma 1 harfine göre değişir. Eğer 'a' harfi bir kez gelirse, 1 olacaktır, (.... 000001).
Taylor Halliday

2
@Ivan Adam, ben de aynı şeyi düşünüyordum. Seçilen cevap bile operatörler hakkında açıklama yapmadı. Detaylı bilgi için teşekkürler.
WowBow

Yukarıdaki benzersiz kontrol sadece Sıralama karakter kümesi (abcd ... z) ile çalışır varsayalım? ile değil (bcad ...)
abdul rashid

"Okuduğum aynı kitaptan bu kodu aldığınızdan şüphe duyuyorum" burada da aynı :) :) beni güldürdü
omurga

39

Tüm bu cevaplar bu nasıl çalıştığını açıklamak düşünüyorum, ancak ben nasıl daha iyi gördüm, bazı değişkenleri yeniden adlandırarak, bazılarını ekleyerek ve ona yorum ekleyerek benim girdi vermek gibi hissettim:

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
Harika bir açıklama. Teşekkür ederim!
Hormigas

Temizleyin explanation..Thank
Prabhaker A

Harika bir açıklama. Anlaması kolay. Teşekkür ederim
Anil Kumar

Bu en iyisi
Vladimir Nabokov

Yorumların icat edilmesinin nedeni budur.
Bay Suryaa Jha

30

Örneğinizin Kod Röportajını Kırma kitabından geldiğini ve cevabımın bu bağlamla ilgili olduğunu varsayıyorum .

Sorunu çözmek için bu algoritmayı kullanmak için, karakterleri yalnızca a'dan z'ye (küçük harf) geçireceğimizi itiraf etmeliyiz.

Sadece 26 harf olduğundan ve bunlar kullandığımız kodlama tablosunda düzgün bir şekilde sıralandığından, bu, tüm potansiyel farklılıkların str.charAt(i) - 'a'32'den (int değişkeninin boyutu) daha düşük olacağını garanti eder checker.

Snowbear tarafından açıklandığı checkergibi , değişkeni bir bit dizisi olarak kullanmak üzereyiz . Örnek olarak bir yaklaşıma sahip olalım:

Diyelimki str equals "test"

  • İlk geçiş (i = t)

denetleyici == 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)
  • İkinci geçiş (i = e)

denetleyici == 524288 (00000000000010000000000000000000)

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

ve böyle devam eder .. koşul aracılığıyla belirli bir karakter için önceden belirlenmiş bir bit bulana kadar

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

Umarım yardımcı olur


2
Geri kalan IMO'dan çok daha iyi bir açıklama ama hala alamadım bir şey denetleyici = 00000000000010000000000000000000 | 00000000000000000000000000010000 o kadar da bitmiş değil | = VEYA operatör. o zamandan bu yana bir değer seçmez mi? neden her iki biti kullanıyor ve ayarlıyor?
CodeCrack

@CodeCrack bunun VEYA bitsel olduğunu söylediniz. Bit düzeyinde değil bit düzeyinde karşılaştırır. İnt is bit Array
MusicMan

7

Yukarıda verilen birkaç mükemmel cevap var. Bu yüzden zaten her şeyin söylediklerini tekrarlamak istemiyorum. Ama aynı programda çalıştığım ve birkaç sorum olduğu için yukarıdaki programa yardımcı olacak birkaç şey eklemek istedim, ancak biraz zaman geçirdikten sonra bu program hakkında daha fazla netliğe sahibim.

Her şeyden önce "checker", tekrarlanan karakterlerin olup olmadığını görmek için Dize'de zaten geçen karakteri izlemek için kullanılır.

Şimdi "checker" bir int veri tipidir, bu nedenle sadece 32 bit veya 4 bayt (platforma bağlı olarak) içerebilir, bu nedenle bu program sadece 32 karakterlik bir karakter kümesi için doğru şekilde çalışabilir. Bu nedenle, bu programın sadece küçük harflerle çalışmasını sağlamak için her bir karakterden 'a' çıkarır. Ancak küçük ve büyük harfleri karıştırırsanız çalışmaz.

Bu arada, her karakterden 'a' çıkarmazsanız (aşağıdaki ifadeye bakın), bu program yalnızca büyük harfli Dize veya yalnızca küçük harfli Dize için düzgün çalışır. Dolayısıyla, yukarıdaki programın kapsamı sadece küçük harflerden büyük harflere kadar artar, ancak birlikte karıştırılamazlar.

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

Ancak, büyük harf, küçük harf, sayı veya herhangi bir özel karakter için endişelenmeden herhangi bir ASCII karakteri için çalışması gereken Bitwise İşlemini kullanarak genel bir program yazmak istedim. Bunu yapmak için, "denetleyicimiz" 256 karakter (ASCII Karakter Kümesi boyutu) depolayacak kadar büyük olmalıdır. Ancak Java'daki bir int, yalnızca 32 bit saklayabileceğinden çalışmaz. Bu nedenle, aşağıdaki programda, bir BitSet nesnesini başlatırken herhangi bir kullanıcı tanımlı boyut geçirilebilen JDK'da bulunan BitSet sınıfını kullanıyorum.

İşte Bitwise operatörü kullanılarak yazılmış yukarıdaki programla aynı şeyi yapan bir programdır, ancak bu program ASCII karakter kümesinden herhangi bir karaktere sahip bir String için çalışacaktır.

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
Bu çözümü arıyordum, ancak iki BitSet değişkenine gerek yok. Sadece izci yeter. Döngü kodu için güncellendi: 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'ın yukarıdaki cevabı okumak bana gerçekten yardımcı oldu, ancak biraz farklı ifade ettim.

<<İçinde (1 << val)biraz değişen operatörüdür. Bu alan 1(ikili olarak temsil edilir 000000001ve sola doğru kaymalar bu tane gibi hafıza ile ayrılan / sıfır, önceki şekilde birlikte) valboşluk. Biz az'yi sadece varsayarak ve çıkarılmasıyla konum beri aher seferinde, her harf içinde sağdan o harfin endeksi olacak 0-25 değerine sahip olacak checkerbiz hareket edecek beri, integer en boolean temsil 1solachecker val .

Her kontrolün sonunda |=operatörü görüyoruz . Bu iki ikili sayıyı birleştirir ve bu dizinde işlenenlerden biri varsa tüm 0'leri 1' ile değiştirir 1. Burada, bu yerde demek olduğunu 1var (1 << val), 1kopyalanır olacak checker, tüm ederkenchecker 'ın mevcut 1'ler korunacaktır.

Tahmin edebileceğiniz gibi, 1burada bir işlev boole bayrağı olarak doğrudur. Dizede bir karakterin zaten temsil edilip edilmediğini kontrol ettiğimizde checker, bu noktada, 1zaten temsil edilmiş olan karakterlerin dizinlerinde esasen bir dizi boole bayrağı ( değerler) olan ve aslında bir dizi olan a ile boole değerleri1 geçerli karakterin dizininde bayrak bulunan .

&Operatör bu çeki yapar. Benzer |=, &operatör bir üzerine kopyalar 1 sadece iki işlenen bir varsa 1o dizinde. Bu nedenle, esasen, yalnızca halihazırda checkertemsil edilen bayraklar da temsil (1 << val)edilir. Bu durumda, yalnızca geçerli karakter zaten temsil edilmişse 1, sonucun herhangi bir yerinde bir hediye olacaktır checker & (1 << val). Ve 1bu işlemin herhangi bir yerinde a varsa , döndürülen boole değeri kullanılır > 0ve yöntem false değerini döndürür.

Sanırım, bit vektörlerine neden bit dizileri de deniyor . Çünkü, dizi veri türünde olmasalar da, boole bayraklarını saklamak için dizilerin kullanılma şekline benzer şekilde kullanılabilirler.


1
Çok yararlı, java bilgi sprinkles için teşekkürler.
Bachiri Taoufiq Abderrahman

4

Basit Açıklama (aşağıdaki JS kodu ile)

  • Makine kodu başına bir tamsayı değişkeni 32 bitlik bir dizidir
  • Tüm bilge işlemler 32-bit
  • OS / CPU mimarisinin veya dilin seçilen sayı sisteminin DEC64agnostiği , örneğin JS için.
  • Bu çoğaltma bulma yaklaşımı, karakterleri 32 boyutunda bir dizide depolamaya benzer; burada, dizede 0thbulursak dizini ayarlarız a, 1stçünküb & vb.
  • Dizedeki yinelenen bir karakterin karşılık gelen biti dolu olacak veya bu durumda 1 olarak ayarlanacaktır.
  • Ivan zaten açıkladı : Bu endeks hesaplamasının önceki cevapta nasıl çalıştığı .

İşlemlerin özeti:

  • & Arasında AND işlemi gerçekleştirincheckerindexKarakterin karakteri
  • Dahili olarak ikisi de Int-32-Arrays
  • Bunlar arasında biraz akıllıca bir işlemdir 2.
  • Kontrol ifoperasyonun çıktısı1
  • Eğer output == 1
    • checkerDeğişken sahip bu özel indeks-inci bit her iki dizide de set
    • Bu bir kopya.
  • Eğer output == 0
    • Bu karakter şu ana kadar bulunmadı
    • Karakterin & karakteri arasında VEYA işlemi gerçekleştirmecheckerindex
    • Böylece, dizin-bit bitini 1
    • Çıkışı şu adrese atayın: checker

Varsayımlar:

  • Tüm küçük harfleri alacağımızı varsaydık
  • Ve bu 32 beden yeterli
  • Dolayısıyla, bizim endeks sayım başladı referans olarak 96 dikkate noktası ASCII kodunu aDİR97

Aşağıda JavaScript kaynak kodu verilmiştir.

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

Not JS, 64 bit olmak tamsayılar rağmen, biraz akıllıca operasyon her zaman 32 bit üzerinde yapıldığını.

Örnek: Dize o aazaman:

// 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

Kod satır satır ayrılmak sağlar.

int denetleyicisi = 0; Yinelenen değerleri bulmamıza yardımcı olacak bir denetleyici başlatıyoruz.

int val = str.charAt (i) - 'a'; Dizenin 'i' konumunda karakterin ASCII değerini alıp 'a' ASCII değeri ile çıkarıyoruz. Varsayım, dizenin yalnızca daha düşük karakterler olması nedeniyle, karakter sayısı 26 ile sınırlıdır. Bu nedenle, 'val' değeri her zaman> = 0 olacaktır.

eğer ((checker & (1 << val))> 0) yanlış döndürürse;

denetleyici | = (1 << val);

Şimdi bu zor kısım. "Abcda" dizesini içeren bir örneği ele alalım. Bu ideal olarak yanlış döndürmelidir.

Döngü yinelemesi 1 için:

Denetleyici: 00000000000000000000000000000000

val: 97-97 = 0

1 << 0: 00000000000000000000000000000001

denetleyicisi & (1 << val): 00000000000000000000000000000000> 0 değil

Bu nedenle denetleyici: 00000000000000000000000000000001

Döngü yinelemesi 2 için:

Denetleyici: 00000000000000000000000000000001

val: 98-97 = 1

1 << 0: 00000000000000000000000000000010

denetleyicisi & (1 << val): 00000000000000000000000000000000> 0 değil

Bu nedenle denetleyici: 00000000000000000000000000000011

Döngü yinelemesi 3 için:

Denetleyici: 00000000000000000000000000000011

val: 99-97 = 0

1 << 0: 00000000000000000000000000000100

denetleyicisi & (1 << val): 00000000000000000000000000000000> 0 değil

Bu nedenle denetleyici: 00000000000000000000000000000111

Döngü yinelemesi 4 için:

Denetleyici: 00000000000000000000000000000111

val: 100-97 = 0

1 << 0: 00000000000000000000000000001000

denetleyicisi & (1 << val): 00000000000000000000000000000000> 0 değil

Bu nedenle denetleyici: 00000000000000000000000000001111

Döngü yinelemesi 5 için:

Denetleyici: 00000000000000000000000000001111

val: 97-97 = 0

1 << 0: 00000000000000000000000000000001

denetleyicisi ve (1 << val): 00000000000000000000000000000001> 0

Bu nedenle yanlış döndürür.


val: 99-97 = 0 val: 99-97 = 2 olmalı ve val: 100-97 = 0 3 olmalı
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

Önceki Mesajlar kod bloğunun ne yaptığını açıklıyor ve BitSet java Veri yapısını kullanarak basit Çözümümü eklemek istiyorum:

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 kullanarak anlama şeklim. Girdi varsayılıyorvar inputChar = "abca"; //find if inputChar has all unique characters

Hadi başlayalım

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

Satırın üstünde Ascii'de a , a = 97 olan inputChar'daki ilk karakterin İkili değerini bulur , sonra 97'yi ikiliye dönüştürür 1100001 olur .

Javascript Ör: "a".charCodeAt().toString(2) 1100001 ile döner

checker = 0 // ikili 32 bit gösterim = 0000000000000000000000000

checker = 1100001 | checker; // denetleyici 1100001 olur (32 bit gösterimde 000000000 olur ..... 00001100001)

Ama benim bitmask ( int checker) sadece bir bit ayarlamak istiyorum , ama denetleyicisi 1100001

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

Şimdi yukarıdaki kod kullanışlı geliyor. Ben her zaman 97 çıkarıyorum (ASCII val 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

Kullanımını sağlar valsıfırlanır

5. satır ve 6. satır iyi açıklanmıştır.


0

Herkes bit vektör kullanarak bir dize benzersiz karakter kotlin eşdeğer arıyor durumunda

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
}

Ref: https://www.programiz.com/kotlin-programming/bitwise

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.