#639. Decode Ways II
題目來源:
https://leetcode.com/problems/decode-ways-ii/description/
題意分析:
存在一個映射把"A"-"Z"映射到1-26。給定一串包含數字喝和"*"的字符串,'*'可以表示1-9的任意數字。求出解碼爲字母的方式的數目。返回結果對10^9+7取餘。
hint:這題是#91. Decode Ways的延伸,建議先做了91題。
例子:
題目思路:
(如果不想看前面超時的解可以直接拉到最後看AC的解)
因爲在91題已經實現了無"*"求解的代碼,這題想着只要把"*"逐一替換爲1-9,求出所有可能的字符串,然後每個字符串分別計算並求和即可。可惜提交後是超時的,我輸出所有的字符串,發現可以以每9個字符串爲一組,第1個即某個"*"替換成了1,第2個即某個"*"替換成了1,以此類推。其中第3、4、5、6求到的數目是相同的(因爲無論前一位是什麼都不影響),第7,8,9也是相同的(3,4,5,6可能會跟前面組成23,24,25,26.而7,8,9不行,所以兩者不同),第1、2特殊要獨立求。
舉個栗子:輸入爲"1**1",那麼部分輸出爲
那麼就可以優化一丟丟。
class Solution {
private:
vector<string> all_str;
public:
int numDecodings(string s) {
bool flag=true;
for (int i=0; i<s.size(); i++){
if (s[i]=='*'){
flag=false;
break;
}
}
if (flag) return numDecodingsConfirm(s);
generate(s,0);
int res=0;
// cout<<all_str.size()<<endl;
for (int i=0; i<all_str.size();i+=9){
int temp=numDecodingsConfirm(all_str[i]);
// cout<<all_str[i]<<" "<<temp<<endl;
res=(res+temp)%100000007;
temp=numDecodingsConfirm(all_str[i+1]);
// cout<<all_str[i]<<" "<<temp<<endl;
res=(res+temp)%100000007;
temp=numDecodingsConfirm(all_str[i+2]);
// cout<<all_str[i+2]<<" "<<temp<<endl;
res=(res+temp*4)%100000007;
temp=numDecodingsConfirm(all_str[i+6]);
// cout<<all_str[i+6]<<" "<<temp<<endl;
res=(res+temp*3)%100000007;
}
// for(int i=0; i<all_str.size(); i++){
// int temp=numDecodingsConfirm(all_str[i]);
// cout<<all_str[i]<<" "<<temp<<endl;
// res=(res+temp)%100000007;
// }
return res;
}
void generate(string& s, int idx){
if (idx==s.size()){
// cout<<s<<endl;
all_str.push_back(s);
return ;
}
if (s[idx]!='*')
generate(s,idx+1);
else{
for(int i=1; i<=9; i++){
s[idx]=i+'0';
generate(s,idx+1);
s[idx]='*';
}
}
return ;
}
int numDecodingsConfirm(string s) {
if (s.size()<=1){
if(s.size()==1&&s[0]=='0'||s.size()==0)
return 0;
else
return 1;
}
vector<int> res(s.size(),0);
int count=s[0]-'0';
count=count*10+s[1]-'0';
if (count<=9){
res[0]=0; res[1]=0;
}
else if (count==10||count==20){
res[0]=1;res[1]=1;
}
else if (count<=26){
res[0]=1;res[1]=2;
}
else if (count%10==0){
res[0]=1;res[1]=0;
}
else{
res[0]=1;res[1]=1;
}
for (int i=2; i<s.size(); i++){
if (s[i]!='0')
res[i]=res[i-1];
if(s[i-1]=='1'||s[i-1]=='2'&&s[i]<='6')
res[i]+=res[i-2];
}
return res[s.size()-1];
}
};
很遺憾的是還是超時了。
Discuss裏大佬的解
class Solution {
public:
int numDecodings(string s) {
long e0 = 1, e1 = 0, e2 = 0, f0, f1, f2;
for ( char c : s ) {
if ( '*' == c ) {
f0 = 9 * e0 + 9 * e1 + 6 * e2;
f1 = f2 = e0;
} else {
f0 = int(c > '0') * e0 + e1 + int(c < '7') * e2;
f1 = '1' == c ? e0 : 0;
f2 = '2' == c ? e0 : 0;
}
e0 = f0 % 1000000007;
e1 = f1;
e2 = f2;
}
return int(e0);
}
};
代碼解釋
字符串從左往右遍歷,每個階段中
- e0表示當前結尾可以是任意字符且結尾不成雙的解碼方式數目
- e1表示當前結尾只能是1,且這個1需要跟後面的數字成雙的解碼方式數目
- e2表示當前結尾只能是2,且這個2需要跟後面的數字成雙的解碼方式數目
用f0、f1、f2更新e0、e1、e2 。對於字符c
- 如果c=='*',f0 = 9 * e0 + 9 * e1 + 6 * e2。很容易理解,f1 = f2 = e0;相當於在末尾加上一個1或者2,數目不變。
- 如果c!='*', 可以把c當做單獨的一個數字,只要c!=‘0’(+e0);如果前一位是'1',c無論是什麼都可以跟前一位成雙(+e1);如果前一位是'2',那麼c只能是'0'-'6'才能跟前一位成雙(+e2),所以總的式子爲
f0 = int(c > '0') * e0 + e1 + int(c < '7') * e2;
此時如果c=='1',那麼f1=e0,即把c附加在後面,否則f1=0.
f2同理。
最後返回e0即可。