SP394 ACODE - Alphacode 详细解析
分类:动态规划 难度:简单
题目来源:https://www.spoj.com/problems/ACODE/
建议用时:18mins
id: zoj2202 poj2033 hdu1508 sp394 spoj
题面:
Alice and Bob need to send secret messages to each other and are discussing ways to encode their messages:
Alice: “Let’s just use a very simple code: We’ll assign ‘A’ the code word 1, ‘B’ will be 2, and so on down to ‘Z’ being assigned 26.”
Bob: “That’s a stupid code, Alice. Suppose I send you the word ‘BEAN’ encoded as 25114. You could decode that in many different ways!”
Alice: “Sure you could, but what words would you get? Other than ‘BEAN’, you’d get ‘BEAAD’, ‘YAAD’, ‘YAN’, ‘YKD’ and ‘BEKD’. I think you would be able to figure out the correct decoding. And why would you send me the word ‘BEAN’ anyway?”
Bob: “OK, maybe that’s a bad example, but I bet you that if you got a string of length 5000 there would be tons of different decodings and with that many you would find at least two different ones that would make sense.”
Alice: “How many different decodings?”
Bob: “Jillions!”
For some reason, Alice is still unconvinced by Bob’s argument, so she requires a program that will determine how many decodings there can be for a given string using her code.
Input
Input will consist of multiple input sets. Each set will consist of a single line of at most 5000 digits representing a valid encryption (for example, no line will begin with a 0). There will be no spaces between the digits. An input line of ‘0’ will terminate the input and should not be processed.
Output
For each input set, output the number of possible decodings for the input string. All answers will be within the range of a 64 bit signed integer.
Example
Input: 25114 1111111111 3333333333 0 Output: 6 89 1
题意:已知加密关系1=A,2=B,……26=Z(2位数字必须两两相邻),然后给一串数字要求解密,求这串完整的数字能解密成几个单词。
多组输入:是
编译语言:C
分析:
一个字母可以由1位数字解密得到,即1~9对应A~I,我们称为A类,也可以由相邻2位数字解密得到,即10~26对应J~Z,我们成为J类。
相邻两位结合,必须保证结合后的数字合法,即在范围00~26之间,大于26的数字不能结合。
字符可以相邻两两结合,但是结合方式可以向前,也可以向后,这里我们选择向前结合。
我们用数组dp[5005]来储存解密结果有几种,因为最多5000个数字,所以数组元素个数只需要不小于5000个即可。
从第1个字符开始看,前1个字符只有1位,则dp[1]最多只能有1种A类解密结果,dp[1]=1。最前2个字符的解密结果和第1位有关,如果第2个字符不能与第1个字符结合,则这两个字符只能各自对应1个A类字母,因此前2个字符dp[2]只能解密出1个单词,它含有2个A类字母dp[2]=1;如果第2位可以与第1位结合,则2两位字符既可以解密成具有1个J类字母的单词,也可以解密成具有2个A类字母的单词(刚才我们讨论过了),共2种,dp[2]=2。
继续看最前3个字符,前3个字符的解密结果和前2位的解密结果有关,如果第3个字符不能与第2个字符结合,就只能让它单独对应1个A类字母,则这3个字符的解密结果就是在前dp[2]的单词上加了1个字母,因此和前2种的解密结果一样多dp[3]=dp[2]。如果第3个字符可以与第2个字符结合,那么前3位字符既可以解密成的单词有2种,一是2字母单词(1个A类字母,1个J类字母),即dp[1]种,dp[3]=dp[1]。2是3字母单词(3个A类字母),dp[3]=dp[2]。共计dp[3]=dp[1]+dp[2]。
至此我们发现,该问题具有最优子结构和重叠子问题的特性。每一种结果都建立在前一种最优结果的基础上,且子问题都是重复出现的同样的问题。因此使用动态规划。
样例图解:
对样例25114进行解析,总共5个字符,分5步进行,每一步的解密方式都列出来,单词与单词之间用逗号隔开。
再以11111为例分析
我们发现,它们的关系满足dp[i] = dp[i-1] + dp[i-2]。
综合考虑:
0的结合性比较特殊,如果它在i位,向左结合时,作用是后导0,则i-1位和i位组成J类字母,则解密的结果就是在前i-2位的字母上再加1个J类字母,即dp[i]=dp[i-2];如果它被右边的j位置结合时,作用是前导0,可以忽略,即解密的结果和前一位的结果一样,dp[j] = dp[j-1],
因此,状态转换关系如下:
dp[i] = dp[i-1] (x>26 或有前导0)
dp[i] = dp[i-2](有后导0)
dp[i] = dp[i-1] + dp[i-2] (常规情况)
AC代码:
#include<stdio.h> #define N (int)5e5 long long dp[N]={1}; long long get(int i) { if(i<0)return 1;/*防止越界访问*/ return dp[i]; } int main() { char s[5001]; while(~scanf(" %s",s) && s[0]!='0') { int i=1; while(s[i]) { int x = (s[i-1]-'0')*10 + s[i]-'0'; if(x>26 || s[i-1]=='0')dp[i]=get(i-1); else if(s[i]=='0')dp[i] = get(i-2); else dp[i] = get(i-1) + get(i-2); i++; } printf("%lld\n",dp[i-1]); } return 0; }