查找字母组合的数量,使每个字母等于一个数字

时间:2016-08-05 23:31:14

标签: c# algorithm

我试图使用C#找出以下算法: a = 1,b = 2,c = 3等z。当给出一串数字时,我需要计算字母组合的数量。例如, 输入:'123',输出 3 ,因为'1'+'2'+'3'= abc,'1'+ 23'= aw,'12'+'3' = LC

我知道应该有一个递归函数来遍历每个数字。在函数内部应该有一个循环。如果数字大于26,则跳过该组合。

这是我到目前为止所尝试的内容:

console.log( myLib(123) )

我知道有逻辑错误。组合列表将在每次通话中重置。创建组合的方式是缺少我无法获得的调整。我不希望写出整个代码。将提供有用的提示。

3 个答案:

答案 0 :(得分:0)

解决方案如下:

  1. 逐步输入您的输入字符串。

  2. 如果输入字符串以数字开头,那就是组合的开头。继续递归继续,其中新输入参数是删除匹配数字的字符串。

  3. 如果输入字符串与数字完全相同,则组合结束。

  4. 我用Python编写了你的​​问题的解决方案。这可以作为你在C#中编写的指南。

    # Create the lookup object
    # chr(97) == 'a'
    lookup = dict()
    for i in range(97, 97+26):
        lookup[str(i-96)] = chr(i)
    
    # Calculate the combinations
    def get_combinations(inp, combinations=set(), fragment=()):
        for key, value in lookup.items():
            if inp == key:
                combinations.add(fragment + (key,))
            elif inp.startswith(key):
                combinations = combinations.union(get_combinations(inp[len(key):], combinations, fragment + (key,)))
        return combinations
    
    combinations = get_combinations('123')
    for combination in combinations:
        str_combination = tuple(lookup[x] for x in combination)
        print(combination, str_combination)
    

    上述程序的输出是:

    ('1', '2', '3') ('a', 'b', 'c')
    ('1', '23') ('a', 'w')
    ('12', '3') ('l', 'c')
    

    ...或者,如果您只对长度感兴趣, 3

答案 1 :(得分:0)

这有效:

Func<int, IEnumerable<string>> combinations =
    n =>
    {
        Func<string, IEnumerable<IEnumerable<string>>> fracture = null;
        fracture = x =>
            String.IsNullOrEmpty(x)
            ? new [] { Enumerable.Empty<string>() }
            : Enumerable
                .Range(1, x.Length)
                .SelectMany(y =>
                    fracture(x.Substring(y))
                    .Select(z =>
                        new [] { x.Substring(0, y) }
                            .Concat(z)));

        return fracture(n.ToString())
            .Select(x => x.Select(y => int.Parse(y) + 'a' - 1))
            .Where(x => x.All(y => y >= 'a' && y <= 'z'))
            .Select(x => String.Join("", x.Select(y => (char)y)));
    };

因此,combinations(123)将产生:

abc 
aw 
lc 

如果您将其视为常规方法,那就是:

public IEnumerable<string> Combinations(int n)
{
    return Fracture(n.ToString())
        .Select(x => x.Select(y => int.Parse(y) + 'a' - 1))
        .Where(x => x.All(y => y >= 'a' && y <= 'z'))
        .Select(x => String.Join("", x.Select(y => (char)y)));
}

public IEnumerable<IEnumerable<string>> Fracture(string x)
{
    return String.IsNullOrEmpty(x)
        ? new [] { Enumerable.Empty<string>() }
        : Enumerable
            .Range(1, x.Length)
            .SelectMany(y =>
                Fracture(x.Substring(y))
                .Select(z =>
                    new [] { x.Substring(0, y) }
                        .Concat(z)));
}

答案 2 :(得分:0)

仅适用于Python中 number 组合的简单(尽管不干净)实现(带记忆):

cache = {}
def num_comb_dp(s):
    if s in cache:
        return cache[s]

    if len(s) <= 2:
        return len(s) if int(s) <= 26 else len(s) - 1

    val = num_comb_dp(s[1:]) + (num_comb_dp(s[2:]) if int(s[0:2]) <= 26 else 0)
    cache[s] = val
    return val

没有备忘录:

def num_comb_no_dp(s):
    if len(s) <= 2:
        return len(s) if int(s) <= 26 else len(s) - 1

    return num_comb_no_dp(s[1:]) + (num_comb_no_dp(s[2:]) if int(s[0:2]) <= 26 else 0)

具有memoization的版本很多更快,如CodeSkulptor link中所示。 在CodeSkulptor here上测试它。

可以在this .NetFiddle中找到C#中的实现。

解决方案基于以下事实:问题涉及重叠子问题(因此也是备忘的候选者)。

让我们来看看这里提供的基本条件:

  1. 长度为1的任何字符串将始终产生一个组合。
  2. 长度为2的字符串将产生至少一个组合(两个单独的数字都可以转换为字母表)。如果字符串的整数值小于等于26(因为我们有26个字母),则只会产生第二个组合
  3. 现在我们已经建立了基本条件,我们可以使用它们来确定我们需要检查的案例。

    字符串的组合可以有两种可能性:

    <COMB> = <digit> + <COMB>
    <COMB> = <digit> + <digit> + <COMB>
    

    对于给定的字符串,有两个可能的组合:一个考虑第一个数字,另一个考虑第一个两个数字。因此,字符串组合的数量将是考虑仅第一个数字的组合数量的以及考虑前两个数字的组合数量

    我们确信第一种情况总会产生一定数量的组合,因为单个数字可以表示为字母表。但是,如果前两位数字形成一个字母,那么第二种情况只会产生组合,即数字<= 26

    现在我们已经把所有这些都放下了,我们可以继续讨论解决方案,可以在this DotNetFiddle及以下找到。代码和注释应该是自我解释的。

        private static int GetNumberOfCombinations(string s)
        {
            // No need of recomputing the value if we already have
            if (cache.ContainsKey(s))
                return cache[s];
    
            // The following combines two conditions:
            // If length is 1, 1 combination possible
            // If length is 2, 1 combination is possible for sure, and 
            // 2nd combination is only valid if first two digits form an alphabet.
            if (s.Length <= 2)
                return int.Parse(s) <= 26 ? s.Length : s.Length - 1;
    
            int value = GetNumberOfCombinations(s.Substring(1));
    
            // If the first two digits form an alphabet,
            // Then only process those combinations
            if (int.Parse(s.Substring(0, 2)) <= 26)
                value += GetNumberOfCombinations(s.Substring(2));
    
            // Store in cache
            cache[s] = value;
    
            return value;
        }