将数字映射到字母

本文关键字:映射 数字 | 更新日期: 2023-09-27 18:00:01

我有一个面试问题问这个:

文本文件有以下行>

            1: A C D
            4: A B
            5: D F
            7: A E
            9: B C  

*每一行都有一个唯一的整数,后跟一个冒号和一个或更多的信件。这些信件是分隔空间(一个或多个)>

                            #2 Write a short program in the language

输出排序的类似的列表

            A: 1 4 7
            B: 4 9
            C: 1 9
            D: 1 5
            E: 7
            F: 5

我不是在找人来解决它,但我总是对这样的问题感到困惑。我想在C#中做这件事,想知道我应该把每一行都存储在2d数组中吗?处理这个问题的最佳方法是什么。存储后,如何用字母而不是数字重新列出每一行?

只是在这里寻找指针。

将数字映射到字母

您可以通过创建Lookup将字母映射到数字集合来解决此问题。您可以使用扩展方法ToLookup来创建Lookup


警告:前方有扰流板

使用LINQ,你可以这样做(在无效输入时中断):

var text = @"1: A C D
4: A B
5: D F
7: A E
9: B C";
var lookup = text
  .Split(new[] { ''r', ''n' }, StringSplitOptions.RemoveEmptyEntries)
  .Select(
    line => new {
      Number = Int32.Parse(line.Split(':').First()),
      Letters = line.Split(':').Skip(1).First().Split(
        new[] {' '}, StringSplitOptions.RemoveEmptyEntries
      )
    }
  )
  .SelectMany(x => x.Letters, (x, letter) => new { x.Number, Letter = letter })
  .OrderBy(x => x.Letter)
  .ToLookup(x => x.Letter, x => x.Number);
foreach (var item in lookup)
  Console.WriteLine(item.Key + ": " + String.Join(" ", item.ToArray()));

如果您熟悉LINQ,下面的代码可以提供您想要的内容:

var result = File.ReadAllLines("inFile").SelectMany(line =>
                {
                    var ar = line.Split(" ".ToCharArray());
                    var num = int.Parse(ar[0].Split(":".ToCharArray())[0]);
                    return ar.Skip(1).Select(s => new Tuple<string, int>(s, num));
                }).GroupBy(t => t.Item1).OrderByDescending(g => g.Count())
                .Select(g => g.Key + ": " + g.Select(t => t.Item2.ToString()).Aggregate( (a,b) => a + " " + b));
            File.WriteAllLines("outFile", result);

我知道你说过你不想要完整的答案,但这种事情很有趣。看起来其他人也提出了类似的解决方案,但这里有另一种表示方式——在"一行"代码中(但有很多括号!):)

var data = @"1: A C D
4: A B
5: D F
7: A E
9: B C";
Console.WriteLine(
    String.Join(
        Environment.NewLine,
        (from line in data.Split(new[] { ''r', ''n' }, StringSplitOptions.RemoveEmptyEntries)
         let lineParts = line.Split(new[] { ':', ' ' }, StringSplitOptions.RemoveEmptyEntries)
         from letter in lineParts.Skip(1)
         select new { Number = lineParts[0], Letter = letter })
        .ToLookup(l => l.Letter, l => l.Number)
        .OrderBy(l => l.Key)
        .Select(l => String.Format("{0}: {1}", l.Key, String.Join(" ", l)))));

哦,我会在生产中写这样的代码吗?可能不会,但这样的运动很有趣!

将帮助您解决的事情

IDictionary<char, IList<int> >

另一个Linq手淫的实现("看马!没有循环!")

using System;
using System.IO;
using System.Linq;
public static class Program
{
    public static void Main(string[] args)
    {
        File.ReadAllLines("input.txt")
            .Select(line => 
            { 
                var split = line.Split(":".ToCharArray(), 2);
                return new { digit = split[0].Trim().Substring(0,1),
                     chars = split[1]
                        .Split(" 't".ToCharArray())
                        .Select(s=>s.Trim())
                        .Where(s => !String.IsNullOrEmpty(s))
                        .Select(s => s[0])
                    };
            })
            .SelectMany(p => p.chars.Select(ch => new { p.digit, ch }))
            .GroupBy(p => p.ch, p => p.digit)
            .ToList()
            .ForEach(g => Console.WriteLine("{0}: {1}", g.Key, string.Join(" ", g)));
    }
}

当然,您可以用ToLookup 替换GroupBy

我将使用Dictionary<string,List<int>>,我将读取输入,并在键a、C、D、a等处向列表中添加1,因此得到结果只是按字母查找。像这样,以一种非深奥的方式:

 string inp = @"1: A C D
            4: A B
            5: D F
            7: A E
            9: B C";
            Dictionary<string, List<int>> res = new Dictionary<string, List<int>>();
            StringReader sr = new StringReader(inp);
            string line;
            while (null != (line = sr.ReadLine()))
            {
                if (!string.IsNullOrEmpty(line))
                {
                    string[] tokens = line.Split(": ".ToArray(),StringSplitOptions.RemoveEmptyEntries);
                    int idx = int.Parse(tokens[0]);
                    for (int i = 1; i < tokens.Length; ++i)
                    {
                        if (!res.ContainsKey(tokens[i]))
                            res[tokens[i]] = new List<int>();
                        res[tokens[i]].Add(int.Parse(tokens[0]));
                    }
                }
            }

res将包含字母->数字列表的结果。

使用拆分(":")和拆分(")进行字符串解析。然后填充

Dictionary<int, List<string>>

并将其翻译成

Dictionary<string, List<int>>

您可以将输入存储在IDictionary中,并对其进行反转以生成输出。

看看这个问题。

我看到已经发布了多个相似(循环)和不太相似(linq)的解决方案,但既然我写了这篇文章,我想我会把它混合起来。

static void Main(string[] args)
{
    var result = new SortedDictionary<char, List<int>>();
    var lines = System.IO.File.ReadAllLines(@"input.txt");
    foreach (var line in lines)
    {
        var split = line.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
        var lineNumber = Int32.Parse(split[0].Substring(0,1));
        foreach (var letter in split.Skip(1))
        {
            var key = letter[0];
            if (!result.ContainsKey(key))
            {
                result.Add(key, new List<int> { lineNumber });
            }
            else
            {
                result[key].Add(lineNumber);
            }
        }
    }
    foreach (var item in result)
    {
        Console.WriteLine(String.Format("{0}: {1}", item.Key, String.Join(" ", item.Value)));
    }
    Console.ReadKey();
}

面试过程的一个重要部分是询问和验证假设。尽管您的描述指出文件的结构是一个后面跟着字母的整数,但您给出的示例显示了整数的递增顺序。如果是这样的话,您可以避免所有LINQ的疯狂,并实现一个更有效的解决方案:

var results = new Dictionary<char, List<int>>();
foreach (var line in File.ReadAllLines(@"input.txt"))
{
    var split = line.Split(new []{' '}, StringSplitOptions.RemoveEmptyEntries);
    var num = int.Parse(split[0].TrimEnd(':'));
    for (int i = 1; i < split.Length; i++)
    {
        char letter = split[i][0];
        if (!results.ContainsKey(letter))
            results[letter] = new List<int>();
        results[letter].Add(num);
    }
}