累加數是一個字符串,組成它的數字可以形成累加序列。
一個有效的累加序列必須至少包含 3 個數。除了最開始的兩個數以外,字符串中的其他數都等於它之前兩個數相加的和。
給定一個只包含數字 '0'-'9'
的字符串,編寫一個算法來判斷給定輸入是否是累加數。
說明: 累加序列裏的數不會以 0 開頭,所以不會出現 1, 2, 03
或者 1, 02, 3
的情況。
示例 1:
輸入:"112358"
輸出: true 解釋: 累加序列爲:1, 1, 2, 3, 5, 8
。1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8
示例 2:
輸入:"199100199"
輸出: true 解釋: 累加序列爲:1, 99, 100, 199。
1 + 99 = 100, 99 + 100 = 199
進階:
你如何處理一個溢出的過大的整數輸入?
思路
3個要點:
1、如何處理過大的溢出的數。
不用數的加法,直接用字符串進行相加運算,最後判斷字符串是否相同。所以需要一個字符串相加的輔助函數。
2、如何遞歸
數字累加和等於後面一個數,看上去像是猆波那契數列,是可以用遞歸來實現的。也即需要將一個問題劃分爲一個子問題。
遞歸方法:
例如“ABCDEF”,假設A+B=C,第一次劃分,A、B、CDEF,A+B!=CDEF,此時可從CDEF中找出是否有前A、B的和,如果有,也即C,那麼前面就可以是符合條件的串,接下來就看B、C、DEF能不能拆成符合條件的劃分,由於少了一個元素,而且將前2個元素也劃分好了,也即我們的問題變成了一個子問題。如此就可以一直遞歸下去。
需要注意的是遞歸只是用來驗證某種初始劃分的基礎上,繼續對後續元素劃分能否符合要求。所以,還需要一個初始劃分的循環給遞歸一個輸入狀態。初始狀態的集合要確保是全面的。
3、如何劃分初始狀態
將整個數組劃分爲3段,然後交給遞歸help函數去判斷能否符合累加規律。等同於在數組中插入2個隔板將數組分成3段,共有。用代碼來實現,一個二重循環即可,循環的計數從[0,n-1),表示在當前元素後面插入隔板。內循環的起始值從外循環當前計數值加1開始。
代碼如下:
bool isAdditiveNumber(string num) {
for(int i=0; i+2<=num.size(); i++){
for(int j=i+1; j+2<=num.size(); j++){
if(check(num.substr(0,i+1), num.substr(i+1,j-i), num.substr(j+1))) return true;
}
}
return false;
}
bool check(string num1, string num2, string num){
if(num1.size()>1 && num1[0]=='0' || num2.size()>1 && num2[0]=='0') return false;
string sum=add(num1, num2);
if(num==sum) return true;
if(num.size()<=sum.size() || sum.compare(num.substr(0,sum.size()))!=0) return false;
//或者用下面這條替換上面語句
//if(num.size()<=sum.size() || sum!=num.substr(0,sum.size())) return false;
else return check(num2, sum, num.substr(sum.size()));
}
string add(string n, string m){
string res;
int i=n.size()-1, j=m.size()-1, carry=0;
while(i>=0 || j>=0){
int sum=carry+(i>=0 ? (n[i--]-'0') : 0) + (j>=0? (m[j--]-'0') : 0);
res.push_back(sum%10+'0');
carry=sum/10;
}
if(carry) res.push_back(carry+'0');
reverse(res.begin(), res.end());
return res;
}
注意事項:
check函數中不對第3個字符串即num判斷是否是以0開頭,因爲num可能是多個字符串,比如"000",人家想表達的是0+0=0,這個是不違反規則的。
注意 i+2<=num.size()與i<=nums.size()-2的區別。
無符號數減一個大於自身的數會翻轉變成一個非常大的數,循環會越界,甚至死循環。
優化初始狀態:
第3個串長度應該不小於前2個串。所以可以限定下前2個串的長度,修改計數器含義,令計數器爲串1和串2長度,最小長度均爲1,剩下的爲串3元素。可以讓串1長度小於等於數組長度一半,然後讓串2長度小於等於剩下數組的一半。如果還要再優化,可能出現初始集合不全的情況。代碼如下:
bool isAdditiveNumber(string num) {
for(int i=1; i<=num.size()/2; i++){
for(int j=1; j<=(num.size()-i)/2; j++){
if(check(num.substr(0,i), num.substr(i,j), num.substr(i+j))) return true;
}
}
return false;
}
算法並非自己想出來。乃是leetcode大神的討論與分享。優化版本的初始集是大神手筆。
鏈接參考