这是一道leetcode上的原题,在笔试面试中也经常遇到。一般我们只要求出所有的解码种类数目就可,并没有要求输出所有的解码序列,这里运用DFS解决了这个问题。题目描述如下:
现在我们先求解一下编码方法数。通过题目我们可以知道,编码是通过一个或两个字符来实现,对于一个编码“12125”;可以一个一个字符的进行编码,也可以两个来进行,这就类似于我们的爬楼梯问题,一次可以上一个台阶,一次也可以上两个台阶,求所有的方式,何其相似。因此我们也可以运用动态规划来求解本题,只是要加上一些条件判断。一个字符是如果是‘0’,那么不能解码,就依赖于他前一个字符看看能否组成10-26之间的数进行解码,因此对于从0-n位置的字符,他的方法数为f(n)=f(n-1)+f(n-2);即对n位置,他可以选择自己单独解码,也可选择与前面一起解码。具体代码如下:
int numDecodings(string &s)
{
int len = s.size();
if( (len <= 0)|| (len>0&&s[0]=='0'))
return 0;
int *dp_num = new int[len]{};
dp_num[0] = 1;
for (int i = 1; i < len; i++)
{
dp_num[i] = s[i] == '0' ? 0 : dp_num[i - 1];//选取i这一位,只取当前指向的一个数字,如果为0,dp[i-1]=0,否则,取dp[i-1]
if (i >= 1 && (s[i - 1] == '1' || (s[i - 1] == '2'&&s[i] <= '6')))//当前数和当前数的前一个数,看看是否在10-26之间
{
if (i == 1)
dp_num[i] += 1;
else
dp_num[i] += dp_num[i - 2];
}
}
return dp_num[len - 1];
}
当然如果要输出所有的解码方式具体是怎么样,可以进行DFS,比如12125,我们选择1这个字符解码,也可以选择12进行解码,之后2125或125相当于是一个新的字符要进行解码,不断进行搜素,终止条件为到达字符串尾。代码如下:
/*
s表示原始字符串,用引用,通过下标可以获取到字符
temp 用来存储解析好的字符串,最后输出,用引用,一直记录解码后的字符,直至递归返回
start表示从第几个位置的字符开始解析
end表示s.size();
*/
void numDecodings_list(string &s,string &temp,int start,int end)
{
int len = s.size();
if (len <= 0 || (len > 0 && s[start] == '0'))
return ;
if (start == end)
{
num++;
cout <<"第"<<num<<"种解码为:"<< temp << endl;
//temp.clear();不用清除,递归后pop后自己会变为空
return;
}
if (s[start] != '0')//第一个不为0
{
char a = s[start] - '1' + 'a';
temp.push_back(a);
numDecodings_list(s, temp, start + 1, end);//继续递归搜索
temp.pop_back();//再回到第一次调用处,这里弹出后 temp=""为空,可以继续添加
if (start+1<end&&(s[start] == '1' || (s[start] == '2'&&s[start + 1] <= '6')))//注意start+1<end
{
a = (s[start] - '0') * 10 + (s[start + 1] - '1') + 'a';
temp.push_back(a);
numDecodings_list(s, temp, start +2, end);//继续递归搜索
temp.pop_back();
}
}
}
int main(void)
{
string s;
string t;
while (1)
{
cin >> s;
numDecodings_list(s, t, 0, s.size());
t.clear();
num = 0;//全局
}
}
输出如下: