[英]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 = 1
的f
值很明顯。 過渡如下:
我們可以在新位置開始一個新的子序列: f(len, a[i] % 8) += 1
。
我們可以從較短的前綴繼續任何子序列:
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並使用標准整數類型。
f(n, 0)
(考慮所有元素,且模8的總和為0)。 該解決方案的時間復雜度為O(n)
(因為存在O(n)
個狀態,並且每個狀態都有2個轉換)。
注意:如果數字不能包含前導零,則只能向狀態添加一個參數:一個標志,指示子序列的第一個元素是否為零(此序列永遠不應擴展)。 解決方案的其余部分保持不變。
注意:此答案假設您是指連續的子序列。
一個數字可被8
整除的除數規則是,如果該數字的最后三位數可被8整除。使用此方法,可以獲得簡單的O(n)
算法,其中n
是數字中的位數。
N=a_0a_1...a_(n-1)
是N
具有n
位數字的小數表示。 s = 0
a_i a_(i+1) a_(i+2)
,檢查數字是否可被8
整除。 如果是這樣,則將i + 1
添加到序列數,即s = s + i
。 這是因為對於范圍從0..i
k
,所有字符串a_k..a_(i+2)
都可以被8
整除。 i
從0
循環到n-2-1
然后繼續。 因此,如果您有1424968
, 1424968
分割的子序列位於:
i=1
( 424
產生i+1 = 2
數字: 424
和1424
) i=3
( 496
得到i+1 = 4
號: 496
, 2496
, 42496
, 142496
) i=4
( 968
產生i+1 = 5
號: 968
, 4968
, 24968
, 424968
, 1424968
) 注意,將需要一些小的修改以考慮長度小於三位數的數字。
因此,序列總數= 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;
}
但是我們沒有固定的起始索引! 好吧,這很容易解決:
假設兩個序列a
和b
,使得int(a) % 8 = int(b) % 8
和b
是一個后綴a
。 無論序列如何繼續, a
和b
的模數始終保持相等。 因此,足以跟蹤共享模數等於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.