it-swarm.com.de

Anzahl der durch k teilbaren Unterfelder

Ich hatte die folgende Frage in einem Interview und trotz der Tatsache, dass ich eine funktionierende Implementierung gegeben habe, war sie nicht effizient genug. 

Eine Scheibe von Feld A ist ein Paar von ganzen Zahlen (P, Q), so dass 0 ≤ P ≤ Q <N ist. Eine Scheibe (P, Q) von Feld A ist durch K teilbar, wenn die Anzahl A [P ] + A [P + 1] + ... + A [Q-1] + A [Q] ist durch K teilbar.

Die Funktion, die ich schreiben sollte, musste die Anzahl der durch K teilbaren Scheiben zurückgeben. Die erwartete Zeitkomplexität war O (max (N, K)) und die Raumkomplexität war O (K).

Meine Lösung war die einfachste, eine Schleife in einer anderen und überprüfe jede Scheibe: O (n ^ 2)

Ich habe nachgedacht, aber ich kann wirklich nicht herausfinden, wie ich das in O (max (N, K)) machen kann. 

Es kann eine Variante des Subset-Sum-Problems sein, aber ich weiß nicht, wie jedes Subarray gezählt wird. 

BEARBEITEN: Elemente im Array können negativ sein. Hier ist ein Beispiel:

A = {4, 5, 0, -2, -3, 1}, K = 5

Function must return 7, because there are 7 subarrays which sums are divisible by 5
{4, 5, 0, -2, -3, 1}
{5}
{5, 0}
{5, 0, -2, -3}
{0}
{0, -2, -3}
{-2, -3}
20
antoniobg

Da Sie nur an Zahlen interessiert sind, die durch K teilbar sind, können Sie alle Berechnungen modulo K durchführen. Betrachten Sie das Summenfeld S so, dass S[i] = S[0] + S[1] + ... + S[i]. Dann ist (P, Q) ein durch K teilbarer Schnitt, wenn S[P] = S[Q] (denken Sie daran, dass wir alle Berechnungen modulo K durchführen). Sie müssen also für jeden möglichen Wert von [0, ..., K-1] zählen, wie oft er in S erscheint.

Hier ist ein Pseudocode:

B = new array( K )
B[0]++
s = 0
for i = 0 to N - 1
  s = ( s + A[i] ) % K
  B[s]++
ans = 0
for i = 0 to K - 1
  ans = ans + B[i] * ( B[i] - 1 ) / 2

Wenn Sie wissen, dass es sich um x-Zellen in S handelt, die den Wert i haben, möchten Sie die Anzahl der Slices zählen, deren Start in einer Zelle mit dem Wert i beginnt und in einer Zelle mit dem Wert i endet. Diese Zahl ist x ( x - 1 ) / 2. Um Edge-Probleme zu lösen, fügen Sie eine Zelle mit dem Wert 0 hinzu.

Wofür steht x ( x - 1 ) / 2: Nehmen wir an, unser Array ist [4, 5, 0] und die Häufigkeit von 4 als Präfixsumme ist x, in diesem Fall 3. Nun können wir aus dem Wert von x folgern, dass es mindestens x-1-Zahlen gibt, die entweder durch k teilbar sind oder mod k gleich 0 sind. Nun sind die möglichen Gesamt-Arrays dieser x-1-Zahlen 1 + 2 + 3 ... + (x - 1) ist ( ( x - 1 ) * ( ( x - 1 ) + 1 ) / 2. (Standardformel für die Summation von 1 bis N, wobei N für (x - 1) steht.

27
Thomash

Für eine bestimmte Anzahl X...

Die Grundidee:  

the sum from the first element to b = the sum from the first element to a
                                    + the sum of the elements between the two

So:

the sum of the elements between the two = the sum from the first element to b
                                        - the sum from the first element to a

Wenn diese Summen auf der rechten Seite den gleichen Rest haben, wenn sie durch X geteilt werden, werden die Reste aufgehoben und die Summe der Elemente zwischen den beiden wird durch X teilbar. Eine Ausarbeitung:

C = the sum of the elements between the two
B = the sum from the first element to b
A = the sum from the first element to a

Jetzt können wir B in die Form PX + Q und A in die Form RX + S umwandeln, für einige ganze Zahlen P, Q, R und S mit 0 <= Q, S < X. Definitionsgemäß sind Q und S die jeweiligen Reste von B und A, die durch X geteilt werden.

Dann haben wir:

C = (PX + Q) - (RX + S)
C = PX + Q - RX - S
C = PX - RX + Q - S
C = (P-R)X + Q - S

Offensichtlich ist (P-R)X durch X teilbar (das Ergebnis ist einfach (P-R)). Jetzt müssen wir nur noch Q - S durch X teilbar sein, aber seit 0 <= Q, S < X müssen sie gleich sein.

Beispiel:

Lassen Sie B = 13, A = 7, X = 3.

Hier B % X = 1 und A % X = 1.

Wir können B als 4*3 + 1 und A als 2*3 + 1 umschreiben.

Dann C = 4*3 + 1 - 2*3 - 1 = 2*3, der durch 3 teilbar ist.

Ansatz auf hoher Ebene:

Erstellen Sie eine Hash-Map, in der die kumulative Summe aller bisher gespeicherten Zahlen mod X gespeichert wird, und zwar wie oft der Restwert angezeigt wird (erstellt in erwarteter O(n)).

Erhöhen Sie den Wert von 0 um eins - dies entspricht dem Start des Arrays.

Einen Zähler auf 0 initialisieren.

Gehen Sie die Hash-Map durch und fügen Sie nC2 (= value!/(2*(value-2)!) ) zur Zählung hinzu. Der 2, den wir hier wählen, sind die Start- und Endpositionen des Subarrays.

Die Anzahl ist der gewünschte Wert.

Laufzeit:

Erwartete O(n).

Beispiel:

Input:    0  5  3  8  2  1
X = 3

Sum:   0  0  5  8 16 18 19
Mod 3: 0  0  2  2  1  0  1

Map:
  0 -> 3
  2 -> 2
  1 -> 2

Count = 3! / 2*(3-2)! = 3  +
        2! / 2*(2-2)! = 1  +
        2! / 2*(2-2)! = 1
      = 5

Die Subarrays werden sein:

0  5  3  8  2  1
-                     0                 =  0 % 3 = 0
-------------         0 + 5 + 3 + 8 + 2 = 18 % 3 = 0
   ----------         5 + 3 + 8 + 2     = 18 % 3 = 0
      -               3                 =  3 % 3 = 0
            ----      2 + 1             =  3 % 3 = 0
4
JerryGoyal
    private int GetSubArraysCount(int[] A, int K)
    {
        int N = A.Length;
        int[] B = new int[K];
        for (int i = 0; i < B.Length; i++)
        {
            B[i] = 0;
        }
        B[0]++;
        int s = 0;
        for (int i = 0; i < N; i++)
        {
            s = (s + A[i]) % K;
            while (s < 0)
            {
                s += K;
            }
            B[s]++;
        }
        int ans = 0;
        for (int i = 0; i <= K - 1; i++)
        {
            ans += B[i] * (B[i] - 1) / 2;
        }
        return ans;
    }
1
user2444873
static void Main(string[] args)
    {
        int[] A = new int[] { 4, 5, 0, -2, -3, 1 };
        int sum = 0;
        int i, j;
        int count = 0;
        for (i = 0; i < A.Length; i++)
        {
            for (j = 0; j < A.Length; j++)
            {
                if (j + i < 6)
                    sum += A[j + i];
                if ((sum % 5) == 0)
                    count++;

            }
            sum = 0;
        }
        Console.WriteLine(count);
        Console.ReadLine();


    }
1
annya

Danke für deine Lösung , @damluar, aber es ist sehr nett! Ich möchte nur ein paar Kommentare hinzufügen. 

  1. Die Ausgabe sollte 7 und nicht 6 als Ausgabe sein. Da wir 7 Subarrays haben, die durch k wie unten teilbar sind, fügen Sie res += storedArray[0]; hinzu, um das Problem zu beheben.

{4, 5, 0, -2, -3, 1}; {5}; {5, 0}; {5, 0, -2, -3}; {0}; {0, -2, -3}; {-2, -3}

Ref Link

  1. Die Initialisierung von cache[0]++; hängt von der Sprache ab. Wenn Sie C++ verwenden, ist dies erforderlich, aber für Java [ link ] ist dies nicht erforderlich.

Code:

public class HelloWorld{

public static void main(String []args){
    int [] A = new int[] {4,5,0,-2,-3,1};
    int k = 5;
    int ans=0;
    System.out.println(countSubArray(A, k)); // output = 7

}

public static int countSubArray(int [] nums, int k){
    int [] storedArray = new int[k];
    int sum=0, res=0;
    for(int i=0; i<nums.length; i++){
        sum = (((sum + nums[i]) % k) + k) % k;
        res += storedArray[sum];
        storedArray[sum]++;

    }
    res += storedArray[0];
    return res; 
}
}
0
Tung Le

Hier ist eine Java-Implementierung der von @Thomash vorgeschlagenen Lösung. 

Die zweite Schleife ist nicht erforderlich, da wir die Antwort direkt um den aktuellen Wert erhöhen und dann erhöhen können.

Um einen negativen Array-Index zu vermeiden, müssen wir auch die Modulberechnung anpassen.

public static int countSubarrays(int[] nums, int k) {
    int[] cache = new int[k];
    cache[0]++;
    int s = 0, counter = 0;
    for (int i = 0; i < nums.length; i++) {
        s = ((s + nums[i]) % k + k) % k;
        counter += cache[s];
        cache[s]++;
    }

    return counter;
}
0
damluar

Beispiel: -

Eingabefeld

int [] nums = {4,3,1,2,1,5,2};

K ist 3

Aufeinanderfolgende Summe

4,7,8,10,11,16,18

Division über aufeinanderfolgenden Summenfeld durch 3  

1,1,2,1,2,1,0

also haben wir vier 1, zwei 2, eine 0

so wird die Gesamtzählung (4 * 3)/2 + (2 * 1)/2 + (2 * 1)/2 = 8 sein

(4 * 3)/2 kommt aus der Auswahl von zwei beliebigen Einsen von vier, d. H. NC2 = n (n-1)/2

Hier ist das Programm

public static long countSubArrayDivByK (int k, int [] nums) {

    Map<Integer, Integer> modulusCountMap = new HashMap<Integer, Integer>();
    int [] consecSum = new int[nums.length];
    consecSum[0]=nums[0];

    for(int i=1;i<nums.length;i++){
        consecSum[i]= consecSum[i-1] +nums[i];
    }

    for(int i=0;i<nums.length;i++){
        consecSum[i]= consecSum[i]%k;

            if(consecSum[i]==0 && modulusCountMap.get(consecSum[i])==null){
                modulusCountMap.put(consecSum[i], 2);
            }else{
                modulusCountMap.put(consecSum[i], modulusCountMap.get(consecSum[i])==null ? 1 : modulusCountMap.get(consecSum[i])+1);
            }

    }

    int count = 0;

    for (Integer val : modulusCountMap.values()) {
        count = count +  (val*(val-1))/2;
    }

    return count;
}

Optimierte Version von oben

static long customOptimizedCountSubArrayDivByK(int k, int[] nums) {

        Map<Integer, Integer> modulusCountMap = new HashMap<Integer, Integer>();
        int [] quotient = new int[nums.length];
        quotient[0]=nums[0]%3;



        if(quotient[0]==0){
            modulusCountMap.put(quotient[0], 2);
        }else{
            modulusCountMap.put(quotient[0], 1);
        }


        for(int i=1;i<nums.length;i++){
            quotient[i]= (quotient[i-1] + nums[i])%3;


                if(quotient[i]==0 && modulusCountMap.get(quotient[i])==null){
                    modulusCountMap.put(quotient[i], 2);
                }else{
                    modulusCountMap.put(quotient[i], modulusCountMap.get(quotient[i])==null ? 1 : modulusCountMap.get(quotient[i])+1);
                }

        }

        int count = 0;

        for (Integer val : modulusCountMap.values()) {
            count = count +  (val*(val-1))/2;
        }

        return count;
    }
0
M Sach