對於題目的理解,其實也不難,我們並沒有必要把數字真的轉換成它要求的字母,只要得出有多少種分割方法就行了。
這種分割的問題也叫“隔板問題”——在數字之間的縫隙裏插入隔板,看有多少種分法,是一類組合問題。這裏由於受到26個字母的限制,只需要考慮分割之後,每兩個“隔間”內有兩個數字就可以了。也就是說,我們只需要考慮當前數字與它後面的數字的組合是不是在[10,25]內即可,這裏之所以是邊界是10,因爲會出現01,02這種,這種不是有效的2位數,不能轉化爲對應的字母
這麼想的話,實際上該問題已經有點像經典的“爬樓梯”問題了:一次上一階或者兩階,總共有多少種走法。於是自然想到了用動態規劃的解法,關鍵性的問題也就出現了:遞推公式是什麼?
首先我們一定能想到不復雜的情況:
只有一個數字時,不用切割,只有一種對映,dp[0] = 1;
dp[1]的值是多少實際上取決於字符串str[1]和str[0]構成的數字是不是處於[0,25]區間內——是,則dp[1] = 2;否則dp[1] = 1.
這兩條是最容易想到的,不過也確實爲後面的條件判斷打下基礎了。接下來是數學中的歸納法,通俗說是找規律。以題目中的輸入示例12258來看,dp[0] = 1, dp[1] = 2
dp[2] = 3((1,22), (12,2), (1,2,2))
dp[3] = 5 ((12,25), (1,2,25), (1,22,5), (12,2,5),(1,2,2,5))
dp[4]還是5,原因是str[4] = 8與str[3] = 5組合起來大於25,不能對映到一個字符,5和8之間必須切開,所以加上的這個數字8並不影響分割方式。
好了,上面的例子寫完,如果還沒找到規律可以接着往後寫。對於dp問題敏感的選手應該已經想到需要用上dp[i], dp[i-1]這些元素來總結遞推了:
2 <= i < len(str)
if str[i]和str[i-1]構成的數字 < 26:
dp[i] = dp[i-1] + dp[i-2] #等於前兩項之和
else:
dp[i] = dp[i-1] #不受影響直接等於前一項
代碼
public class Solution46m {
public int translateNum(int num) {
String res = String.valueOf(num);
int[] dp = new int[res.length()];
if (res.length() < 2) {
return res.length();
}
dp[0] = 1;
int firstSum = Integer.valueOf(res.substring(0, 2));
dp[1] = firstSum >= 10 && firstSum <= 25 ? 2 : 1;
for (int i = 2; i < res.length(); i++) {
int sum = Integer.valueOf(res.substring(i - 1, i + 1));
if (sum >= 10 && sum <= 25) {
dp[i] = dp[i - 1] + dp[i - 2];
} else {
dp[i] = dp[i - 1];
}
}
return dp[res.length()-1];
}
}