首页 > 试题广场 >

数字字符转化为字母组合的种数

[编程题]数字字符转化为字母组合的种数
  • 热度指数:3207 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定一个字符串str,str全部由数字字符组成,如果str中的某一个或者相邻两个字符组成的子串值在1~26之间,则这个子串可以转换为一个字母。规定‘1’转换为“A”,“2”转换为“B”......"26"转化为“Z“。请求出str有多少种不同的转换结果,由于答案可能会比较大,所以请输出对取模后的答案。

输入描述:
输出一行仅有’0‘~’9‘组成的字符串,代表str 


输出描述:
输出一个整数,代表你所求出的取模后答案。
示例1

输入

1111

输出

5

说明

能转换出来的结果有:“AAAAA”,“LAA”,“ALA”,“AAL”,“LL”。 
示例2

输入

01

输出

0

说明

“0”没有对应的字符,而“01”是不可转换的。 
示例3

输入

18

输出

2

备注:
时间复杂度,空间复杂度
动态规划求解,从左往右试,可以粗略分为以下两种情况
  1. 如果str[i-1]是1,我们需要考虑当前位置str[i]str[i-1]构成个两位数的转化,如果str[i-1]是2,且str[i]为0~6,我们同样需要考虑当前位置str[i]和前一个位置str[i-1]组合成两位数进行转化,需要加上到str[i-2]为止的组合数。此时的状态转移方程为:dp[i]=dp[i-1]+dp[i-2]
  2. 如果前一个位置是0,或者前一个位置是2,且当前位置是大于6的数,那直接从前面的状态转移过来,状态转移方程为:dp[i]=dp[i-1]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br  = new BufferedReader(new InputStreamReader(System.in));
        char[] str = br.readLine().toCharArray();
        if(str.length == 0 || (str.length == 1 && str[0] == '0')) {
            System.out.println(0);        // 一个0转化不成字母
        }else if(str.length == 1){
            System.out.println(1);        // 一个非零数字只能有一种转化
        }else{
            int[] dp = new int[str.length + 1];
            dp[0] = 1;        // 没有字符,初始化为1
            for(int i = 0; i < str.length; i++){
                dp[i + 1] = str[i] == '0'? 0: dp[i];
                // 如果前一个字符是1,或者前一个字符是2且当前字符是0~6,就可以和前一个字符组成两位数的字母转化
                if(i > 0 && (str[i - 1] == '1' || str[i - 1] == '2' && str[i] <= '6')) {
                    dp[i + 1] += dp[i - 1];
                    dp[i + 1] %= 1000000007;
                }
            }
            System.out.println(dp[dp.length - 1]);
        }
    }
}

发表于 2021-11-18 15:23:22 回复(0)

动态规划

  • dp[i] :前 i 个字符的子串,转化为字母组合的方案。
  • dp[i] 的结果与前 i-1 个字符组成的子串相关。
    1. 如果第 i 个字符只能够单独转换为一个字母(比如说 str(i)='8'),则前 i 个子符转化为 字符的方案数 与 前 i-1 个字符的方案数相同。
    2. 如果第 i 个字符能够和第 i-1 个字符组合转换为一个字母,则第 i 个字符有两种转换为 字母 的可能:
      (1) 第 i 个字符单独转换为 字母(和第一种情况一样)
      (2) 结合第 i-1 个字符,组合转换为 字母,如果是这种情况,那么,需要找出第 i-1 个字符单独转换为 字母 的方案数。因此,这种情况下,方案数 与 第 i-1 个字符单独转换为字母的方案数相同。

递推式

// 第 i 个字符只能单独转换为一个 字母
// dp[i-1] :第 i 个字符单独转换的方案数
dp[i]=dp[i-1];
// 第 i 个字符 可以和 第 i-1 个字符 共同转换为一个 字母
// dp[i-1] :第 i 个字符单独转换的方案数
// dp[i-2] :第 i 个字符,结合第 i-1 个字符组合转换为一个字母的方案数
dp[i]=dp[i-1]+dp[i-2];

题解

public class Main {
    private static String num;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        num = in.next();
        num = "0" + num;
        System.out.println(findLetter());
    }
    private static int findLetter() {
        int[] dp = new int[num.length() + 1];
        // 初始化 1:表示第 1 个字符只能有 1 种转换情况
        dp[0] = 1;
        for (int i = 1; i < num.length(); ++i) {
            // 判断第 i 个字符能够转换为 字母的情况
            // 0 不能转换
            // 1 第 i 个字符只能单独转换为一个字母
            // 2 第 i 个字符能够结合第 i-1 个字符转换为 一个字母
            int judge = judge(i);
            if (judge == 0) {
                return 0;
            }
            if (judge == 1) {
                // dudge=1
                dp[i] = dp[i - 1];
            } else {
                // judge=2
                dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007;
            }
        }
        return dp[num.length() - 1];
    }
    // 判断第 i 个字符能够转换为 字母的情况
    private static int judge(int i) {
        int count = 0;
        if (num.charAt(i) >= '1' && num.charAt(i) <= '9') {
            // 能够单独转换
            count++;
        }
        if (num.charAt(i - 1) != '0') {
            int letter = Integer.valueOf(num.substring(i - 1, i + 1));
            if (letter >= 10 && letter <= 26) {
                // 能够结合第 i-1 个字符一起转换
                count++;
            }
        }
        return count;
    }
}
编辑于 2020-05-11 16:11:30 回复(0)
这题使用动态规划的方法解决,主要是找到递推公式。用f(n)表示长度为n的字符串的有效组合数。考虑这个字符串的末尾,有效的组合可以分为两类,一类是末尾是单个数字转化的,一类是末尾是两个数字转化的。那么反过来就可以这样求f(n),
先令f(n) = 0,
如果字符串的最后一个数字可以转化为字母,我们就给f(n)加上f(n-1),
如果字符串的最后两个数字可以转化为字母,我们就给f(n)加上f(n-2)。
实现代码如下:
import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        final long  A = 1000000007;
        Scanner input = new Scanner(System.in);
        String inputString = input.nextLine();
        
        // 如果长度为0或者首字符为0,直接返回
        if (inputString.length() == 0 || inputString.charAt(0) == '0') {
            System.out.println(0);
            return;
        }
        
        int length = inputString.length();
        long fn = 1;
        long fn_1 = 1; 
        long fn_2 = 1; // 初始值要设对,可以通过长度为2的字符串来验证
        
        for (int i = 1; i < length; ++i){
            fn_2 = fn_1;
            fn_1 = fn;
            
            fn = 0;

            if (inputString.charAt(i) != '0'){
            // i这一位单独可以转换
                fn += fn_1;
            }
            int c = (inputString.charAt(i - 1) - '0') * 10 + (inputString.charAt(i) - '0');
            if (c >= 10 && c <= 26){
            // i和i-1可以组合转换
                fn += fn_2;
            }

            if (fn == 0) {
                // 如果某个fn为0,那么结果一定为0
                break;
            }

            fn %= A;
        }
        System.out.println(fn);
    }
}


编辑于 2019-08-10 21:30:13 回复(0)
#include <bits/stdc++.h>
using namespace std;
const int M = 1000000007;

int main(){
    string s;
    cin>>s;
    int l = s.length(), p=1;
    int cnt = (s[l-1]=='0'?0:1);
    for(int i=l-2;i>=0;i--){
        if(s[i]=='0'){
            p = cnt;
            cnt = 0;
        }else{
            int t = cnt;
            if((10*(s[i]-'0')+s[i+1]-'0')<=26)
                cnt = (cnt+p)%M;
            p = t;
        }
    }
    cout<<cnt<<endl;
    return 0;
}

发表于 2020-03-07 01:52:29 回复(0)
#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    int cur = s[s.size() - 1] == '0' ? 0 : 1;
	int next = 1, tmp = 0;
	for (int i = s.size() - 2; i >= 0; i--){
		if (s[i] == '0'){
			next = cur;
			cur = 0;
		}
		else{
			tmp = cur;
			if ((s[i] - '0') * 10 + s[i + 1] - '0' < 27)
				cur = (cur+next)%1000000007;
			next = tmp;
		}
	}
	cout<< cur%1000000007;
    return 0;
}

发表于 2019-08-31 21:57:49 回复(0)
import java.util.*;

public class Main {
    public static int mod = (int)1e9 + 7;
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.nextLine();
        System.out.println(dpComboNum(s));
    }
    /*
    public static long getComboNum (char[] arr, int index) {
        if (index == arr.length)
            return 1;
        if (arr[index] == '0')
            return 0;
        if (arr[index] == '1') {
            long res = getComboNum(arr, index+1) % mod;
            if (index + 1 < arr.length) {
                res += getComboNum(arr, index+2) % mod;
            }
            return res % mod;
        }
        if (arr[index] == '2') {
            long res = getComboNum(arr, index+1) % mod;
            if (index + 1 < arr.length && (arr[index+1] >= '0' && arr[index + 1] <= '6')) {
                res += getComboNum(arr, index+2) % mod;
            }
            return res % mod;
        }
        return getComboNum(arr, index+1) % mod;
    }*/
    public static int dpComboNum(String strs) {
        if (strs == null || strs.charAt(0) == '0' || strs.length() == 0)
            return 0;
        int n = strs.length();
        int[] dp = new int[n+1];
        dp[n] = 1;
        for (int i = n-1; i >= 0; i--) {
            dp[i] = dp[i+1];
            if (strs.charAt(i) == '0')
                dp[i] = 0;
            if (strs.charAt(i) == '1') {
                if (i + 1 < n) {
                    dp[i] = (dp[i] + dp[i+2]) % mod;
                }
            }
            if (strs.charAt(i) == '2') {
                if (i + 1 < n && (strs.charAt(i+1) >= '0' && strs.charAt(i+1) <= '6')) {
                    dp[i] = (dp[i] + dp[i+2]) % mod;
                }
            }
        }
        return dp[0];
    }
}

发表于 2021-07-11 19:30:55 回复(0)
#include <iostream>
#include <vector>
#include <string>
using namespace std;
const int mod = 1e9 + 7;
// int process(int i, string s){
//     if(i == s.size())
//         return 1;
//     if(s[i] == '0')
//         return 0;
//     if(s[i] == '1'){
//         int res = process(i + 1, s);
//         if(i + 2 <= s.size()){
//             res += process(i + 2, s);
//         }
//         return res;
//     }
//     if(s[i] == '2'){
//         int res = process(i + 1, s);
//         if(i + 2 <= s.size() && s[i + 1] >= '0' && s[i + 1] <= '6')
//             res += process(i + 2, s);
//         return res;
//     }
//     return process(i + 1, s);
// }

int process(string s){
    if(s.size() == 0)
        return 0;
    if(s == "0")
        return 0;
    if(s.size() == 1)
        return 1;
    int n = s.size();
    vector<int> v(n + 1);
    v[n] = 1;
    for(int i = n - 1; i >= 0; i--){
        if(s[i] == '0')
            v[i] = 0;
        else if(s[i] == '1'){
            long res = v[i + 1];
            if(i + 2 <= s.size()){
                res += v[i + 2];
            }
            v[i] = res % mod;
        }
        else if(s[i] == '2'){
            long res = v[i + 1];
            if(i + 2 <= s.size() && s[i + 1] >= '0' && s[i + 1] <= '6')
                res += v[i + 2];
            v[i] = res % mod;
        }
        else
            v[i] = v[i + 1];
    }
    return v[0];
}

int main(void){
    string s;
    cin >> s;
    cout << process(s) << endl;
    return 0;
}
从暴力解法 -> 探索动态规划道路求解结果
发表于 2021-05-24 21:49:31 回复(0)
#include<iostream>
#include<cstring>
using namespace std;
const int mod = 1e9+7;
int proccess(string &s,int i) {
    if (i==s.size()) return 1;
    if (s[i] == '0') return 0;
    int res = proccess(s, i+1) % mod;
    if(i+1 < s.size()) {
        int num = (s[i]-'0')*10 + s[i+1]-'0';
        if(num>=0 && num <=26) res = (res + proccess(s,i+2) ) % mod;
    }
    return res;
}
int main() {
    string s;
    cin>>s;
    int n = s.size();
     
    
    if(s[0]==0) {
        puts("0");
        return 0;
    }
    int res = proccess(s,0);
    cout << res << endl;
    
    
    
}

发表于 2021-02-02 16:06:06 回复(0)
#include<cstdio>
(802)#include<cstring>
const int mmax=100010;
char str[mmax];
int dp[mmax];
int main(){
    scanf("%s",str+1);
    dp[0]=1;
    if(str[1]!='0')
        dp[1]=1;
    //printf("%d",strlen(str+1));
    for(int i=2;i<=strlen(str+1);++i){
        if(str[i]!='0'){
           dp[i]=(dp[i]+dp[i-1])%1000000007;        
        }
        int sum=(str[i-1]-'0')*10+str[i]-'0';
        if(sum>=10&&sum<=26){
            dp[i]=(dp[i]+dp[i-2])%1000000007;
        }
        //printf("%d %d\n",i,dp[i]);
    }
    printf("%d",dp[strlen(str+1)]);
}

发表于 2020-03-16 11:26:13 回复(0)