Memoization
Looking into memoization is a good idea - in this case it allows you to achieve \$O(n)\$ runtime performance instead of \$O(n^2)\$\$O(2^n)\$. One way to add memoization is to create a wrapper method that takes the same arguments as Decode. This method first looks up the given arguments in a dictionary, and if they're present, it can return the memoized result. If not, it will call Decode and store its result in the dictionary before returning it.
However, I would recommend removing NumDecodings and rewriting Decode so it only takes a single string parameter. That will simplify memoization and make it a little more efficient. For example, "412123" and "512123" both have the same suffix ("12123"), but because the 'sub-calls' ("412123", 1, 1) and ("512123", 1, 1) use different arguments (and thus different memoization keys) you won't benefit from memoization across those different inputs.
Other notes
- Instead of passing the original input string and an offset and length, consider passing 'the rest of the string' instead.
Decodecan then callDecode(s.Substring(1))if the leading digit is a valid unit, andDecode(s.Substring(2))if the leading 2 digits are a valid unit. - There are various things in the current code that can be simplified:
if (i + len - 1 >= s.Length)toif (i + len > s.Length)s.Substring(i, len)[0] == '0'tos[i] == '0'else if (i + len - 1 == s.Length - 1toelse if (i + len == s.Lengthjump1 = i + len - 1 + 1tojump1 = i + lenjump2 = i + len - 1 + 2tojump2 = i + len + 1
- Moving
s.Substring(i, len)[0] == '0'to anelse ifbranch allows you to remove thesub < 27 && sub > 0checks below. - Disabling code during development is normal, but it's a good idea to clean things up from time to time.
- I would rename
Decodeto something more descriptive likeValidDecodingsCount-Decodeimplies that it actually decodes the given input, which is not the case.jump1andjump2aren't very descriptive names either, but it's a bit difficult to come up with better names. Mayberest1Combinationsandrest2Combinations?