簡體   English   中英

查找n位數的子序列數,該子序列可被8整除

[英]Find the number of subsequences of a n-digit number, that are divisible by 8

給定n = 1到10 ^ 5,以十進制格式存儲為字符串。

示例:如果n = 968,則在所有子序列(即9、6、8、96、68、98、968)中,有3個子序列(即968、96和8)可被8整除。因此,答案是3。

由於答案可能非常大,因此請以模為單位打印答案(10 ^ 9 + 7)。

您可以使用動態編程。 f(len, sum)是長度的前綴的子序列的數目len ,使得它們的和是sum模8( sum范圍從0到7)。

len = 1f值很明顯。 過渡如下:

  1. 我們可以在新位置開始一個新的子序列: f(len, a[i] % 8) += 1

  2. 我們可以從較短的前綴繼續任何子序列:

     for old_sum = 0..7 f(len, (old_sum * 10 + a[i]) % 8) += f(len - 1, old_sum) // take the new element f(len, old_sum) += f(len - 1, old_sum) // ignore the new element 

當然,您可以執行所有計算模塊10 ^ 9 + 7並使用標准整數類型。

  1. 答案是f(n, 0) (考慮所有元素,且模8的總和為0)。

該解決方案的時間復雜度為O(n) (因為存在O(n)個狀態,並且每個狀態都有2個轉換)。

注意:如果數字不能包含前導零,則只能向狀態添加一個參數:一個標志,指示子序列的第一個元素是否為零(此序列永遠不應擴展)。 解決方案的其余部分保持不變。

注意:此答案假設您是指連續的子序列。

一個數字可被8整除的除數規則是,如果該數字的最后三位數可被8整除。使用此方法,可以獲得簡單的O(n)算法,其中n是數字中的位數。

  1. N=a_0a_1...a_(n-1)N具有n位數字的小數表示。
  2. 讓到目前為止的序列數為s = 0
  3. 對於每三位數字集a_i a_(i+1) a_(i+2) ,檢查數字是否可被8整除。 如果是這樣,則將i + 1添加到序列數,即s = s + i 這是因為對於范圍從0..i k ,所有字符串a_k..a_(i+2)都可以被8整除。
  4. i0循環到n-2-1然后繼續。

因此,如果您有14249681424968分割的子序列位於:

  1. i=1424產生i+1 = 2數字: 4241424
  2. i=3496得到i+1 = 4號: 496249642496142496
  3. i=4968產生i+1 = 5號: 9684968249684249681424968

注意,將需要一些小的修改以考慮長度小於三位數的數字。

因此,序列總數= 2 + 4 + 5 = 11 總復雜度= O(n) ,其中n是位數。

這不是代碼編寫服務,所以我只給您足夠的方法。

子序列可能有10種狀態。第一個是空的。 第二個是前導0。其他8是一個正在進行的數字,它是0-7 mod8。您從字符串的開頭以1的方式為空開始,別無其他。 在字符串的末尾,您的答案是使前導0加上正在進行的數字0 mod 8的方式的數量。

過渡表應該很明顯。 剩下的只是普通的動態編程。

可以使用以下事實:對於任何三位數的abc ,以下條件成立:

abc % 8 = ((ab % 8) * 10 + c) % 8

換句話說:對於具有固定起始索引的數字的測試可以級聯:

int div8(String s){
    int total = 0, mod = 0;

    for(int i = 0; i < s.length(); i++)
    {
        mod = (mod * 10 + s.charAt(i) - '0') % 8

        if(mod == 0)
            total++;
    }

    return total;
}

但是我們沒有固定的起始索引! 好吧,這很容易解決:

假設兩個序列ab ,使得int(a) % 8 = int(b) % 8b是一個后綴a 無論序列如何繼續, ab的模數始終保持相等。 因此,足以跟蹤共享模數等於8的屬性的序列數。

final int RESULTMOD = 1000000000 + 7;

int div8(String s){
    int total = 0;
    //modtable[i] is the number of subsequences with int(sequence) % 8 = i
    int[] modTable = new int[8];

    for(int i = 0; i < s.length(); i++){
        int[] nextTable = new int[8];

        //transform table from last loop-run (shared modulo)
        for(int j = 0; j < 8; j++){
            nextTable[(j * 10 + s.charAt(i) - '0') % 8] = modTable[j] % RESULTMOD;
        }

        //add the sequence that starts at this index to the appropriate bucket
        nextTable[(s.charAt(i) - '0') % 8]++;

        //add the count of all sequences with int(sequence) % 8 = 0 to the result
        total += nextTable[0];
        total %= RESULTMOD;

        //table for next run
        modTable = nextTable;
    }

    return total;
}

運行時為O(n)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM