题目描述
给定一个字符串str,str全部由数字字符组成,如果str中的某一个或者相邻两个字符组成的子串值在1~26之间,则这个子串可以转换为一个字母。规定'1'转换为'A','2'转换为'B'......'26'转化为'Z'。请求出str有多少种不同的转换结果,由于答案可能会比较大,所以请输出对10^9+7取模后的答案。
输入描述:
输出一行仅有'0'-'9'组成的字符串,代表(0≤length(str)≤100000)。
输出描述:
输出一个整数,代表你所求出的取模后答案。
示例1
输入
1111
输出
5
说明
能转换出来的结果有:"AAAAA","LAA","ALA","AAL","LL"。
示例2
输入
01
输出
0
说明
0没有对应的字符,而"01"是不可转换的。
备注:
时间复杂度O(n),空间复杂度O(1)
方法一:暴力枚举,假设s的长度为N,先定义递归函数process,process(s,i)表示s[0...i-1]已经转换完毕,而s[i...N-1]还没有转换的情况下,最终合法的转换有多少种并返回。
//运行超时 O(2^n)
#include<bits/stdc++.h>
using namespace std;
const int number=1e9+7;
int process(const string& s, int i) {
if (i == s.size()) {
return 1;
}
if (s[i] == '0') {
return 0;
}
int res = process(s, i + 1);
if (i + 1 < s.size() && (s[i] - '0') * 10 + s[i + 1] - '0' < 27) {
res += process(s, i + 2);
}
if(res>number){
res%=number;
}
return res;
}
int main() {
string s;
cin >> s;
cout << process(s, 0) << endl;
system("pause");
return 0;
}
方法二:记忆化搜索,法一中存在大量重复计算,通过记忆化减支,使得每一个process(s,i)只计算一次
//ac:O(n) O(n)
#include<bits/stdc++.h>
using namespace std;
const int number=1e9+7;
vector<int> p;
int process(const string& s,int i){
if(i==s.size()){
return 1;
}
if(s[i]=='0'){
return 0;
}
if(p[i]!=-1){
return p[i];
}
int res=process(s,i+1);
if( i+1<s.size() && (s[i]-'0')*10+s[i+1]-'0' < 27 ){
res+=process(s,i+2);
}
if(res>number){
res%=number;
}
p[i]=res;
return res;
}
int main(){
string s;
cin>>s;
p=vector<int>(s.size(),-1);
cout<<process(s,0)<<endl;
return 0;
}
方法三:将自顶向下的记忆化搜索改成自底向上的动态规划
//dp:O(n) O(1)
#include<bits/stdc++.h>
using namespace std;
const int number=1e9+7;
int main(){
string s;
cin>>s;
int n=s.size();
int cur = s[n-1]=='0' ? 0 : 1;
int aftCur=1;
int tmp=0;
for(int i=n-2;i>=0;i--){
if(s[i]=='0'){
aftCur=cur;
cur=0;
}else{
tmp=cur;
if((s[i]-'0')*10+s[i+1]-'0'<27){
cur+=aftCur;
if(cur>number){
cur%=number;
}
}
aftCur=tmp;
}
}
cout<<cur<<endl;
return 0;
}