[leetcode] Decode Ways

本文探讨了解码方式的计算方法,通过分析最大可组合子串,利用斐波那契数列原理解决编码解码问题。同时,文章提供了解码数量的计算公式和代码实现,包括空间复杂度优化的方法,以及不同解码策略的比较。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

From : https://leetcode.com/problems/decode-ways/

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26

Given an encoded message containing digits, determine the total number of ways to decode it.

For example,
Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12).

The number of ways decoding "12" is 2.

本题是斐波那契数列的变种 。

思路1:寻找最大的可以组合的子串,如“1217201211”中,最大的可以组合的子串是 “1217“,”20” 和 “1211”, 因为7和0后面是不可能有1~26的数字,所以这个串应该分成上面三个子串。然后所有组合的可能结果就是子串可能结果的乘积。  对于任意子串,其可能结果的组合是要么两个一起,要么一个一个的,所以是Fibonacci数列。

子串中,需要考虑特殊的 “110”,“10”等这样以0结尾的子串。0必须和前面一个字符组合,所以组合就减少了2个字符产生的结果。即: “10”如果两个都参与会有两种结果,实际1和0必须在一起,所以只有一种结果;“2210”如果4个都参与组合会有8中情况,但由于1必须和0组合,所以只有4-2=2个字符参与组合,故只有两种情况。

class Solution {
public:
    vector<int> iniFibo(int n) {
        vector<int> f(max(3, n+1));
		f[0] = 0; f[1] = 1; f[2] = 2;
        for(int i=3; i<=n; i++) f[i] = f[i-1]+f[i-2];
        return f;
    }
    int numDecodings(string s) {
        if(s.size() == 0 || s[0]=='0') return 0;
		vector<int> fibs = iniFibo(s.size());

        int len = s.size(), ans=1, last;
        vector<bool> canCombi(len, false); // true表示i处可以和i+1处结合成一个新的字母,11可以是L
        for(int i=0, len1=len-1; i<len1; i++){
			if(s[i]!='1' && s[i]!='2' && s[i+1]=='0') return 0; // 只有1和2后面才能有0
            if(s[i]=='1' || s[i]=='2'&&s[i+1]>='0'&&s[i+1]<='6') {
                canCombi[i]=true;
            }
        }
        for(int k=0; k<len; k++) {
            last = k;
            while(k<len && canCombi[k]) k++;
			// s[k]=='0' 时候,要保证至少有一组,且由于0和前面一个要组合用,所以fibo应该减少2
			int base = (s[k]=='0'?max(k-last-1, 1):k-last+1);
            ans *= fibs[base];
        }
        return ans;
    }
};

public class Solution {
	public int numDecodings(String s) {
		if (s == null || s.equals("") || s.charAt(0) == '0') {
			return 0;
		}
		int[] fibo = initFibo(s.length());
		boolean[] beJoined = new boolean[s.length()];

		for (int i = 0; i < s.length() - 1; ++i) {
			char a = s.charAt(i);
			char b = s.charAt(i + 1);
			if (b == '0' && a != '1' && a != '2') {
				return 0;
			}
			if (a == '1' || a == '2' && b >= '0' && b <= '6') {
				beJoined[i] = true;
			}
		}

		int ans = 1;
		for (int i = 0; i < beJoined.length; i++) {
			int j = i;
			while (i < beJoined.length && beJoined[i]) {
				++i;
			}
			ans *= fibo[i - j + 1 - (s.charAt(i) == '0' ? 2 : 0)];
		}
		return ans;
	}

	private int[] initFibo(int n) {
		int[] f = new int[n + 2];
		f[0] = 1;
		f[1] = 1;
		for (int i = 2; i <= n; ++i) {
			f[i] = f[i - 1] + f[i - 2];
		}
		return f;
	}
}

思路2: 每次对于当前的字符判断是否属于1-9(0肯定不行,因为0不在1-26中),如果属于,那么当前的字符可以被decode,并且和f[n-1]组合,f[n] += f[n-1]. 然后对于当前字符和前一个字符组成的字符串判断是否属于10-26,如果属于,那么这两个字符可以被decode,并且和f[n-2]组合,f[n] += f[n-2]
class Solution {
public:
    int numDecodings(string s) {
        if (s == "" || s[0]=='0') return 0;
        vector<int> f(s.size());
		f[0] = 1;
        int i = 1;
        for (; i < s.size(); i++){
            f[i] = 0;
            if('1'<=s[i] && s[i]<='9') f[i] += f[i-1];
            string tmp(s, i-1, 2);
            if("10" <= tmp && tmp <= "26")
				if(i > 1) { f[i] += f[i-2];}
				else{ f[i] += 1;}//i == 1,没有i - 2这个index
        }
        return f[i-1];
    }
};


将上面代码更改,降低空间复杂度:

class Solution {
public:
    int numDecodings(string s) {
        if (s == "" || s[0]=='0') return 0;
		int cur=1, pre1=1, pre2=1;        
        for (int i = 1; i < s.size(); i++){
			if(s[i]=='0' && s[i-1]=='0') return 0;
			cur = 0;
            if('1'<=s[i] && s[i]<='9') cur += pre1;
            string tmp(s, i-1, 2);
            if("10" <= tmp && tmp <= "26")
				if(i > 1) { cur += pre2;}
				else{ cur += 1;}//i == 1,没有i - 2这个index
			pre2 = pre1;
			pre1 = cur;
        }
        return cur;
    }
};

public class Solution {
	public int numDecodings(String s) {
	    if(s == null || s.length()==0 || s.charAt(0) == '0') {
	        return 0;
	    }
	    int ans = 1;
	    int e1 = 1, e2 = 1;
	    for(int i=1; i<s.length(); ++i) {
	        ans = 0;
	        char c = s.charAt(i);
	        if(c>='1' && c<='9') {
	            ans += e1;
	        }
            if(s.charAt(i-1)=='1' || s.charAt(i-1)=='2' && c<='6') {
                ans += e2;
            }
            e2 = e1;
            e1 = ans;
	    }
	    
	    return ans;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值