Идею решения вы уловили правильно, поэтому останавливаться на подробном алгоритме не стану. А вот реализовать эту идею можно многими способами, включая массу неэффективных во всех отношениях вариантов.
Никогда не торопитесь создавать новый класс. В BCL .NET для большинства распространенных задач все необходимое уже есть, нужно только потратить немного времени на чтение документации (или мастерски владеть Google-Fu =)) .
Если учесть, что от вас скорее всего хотят не однострочное решение, которое вы вряд ли сможете объяснить, то стоило обратить внимание на класс Dictionary.
Тогда подсчет количества одинаковых букв будет выглядеть так:
var inputString = "hghjkgdjfbdgfjafsdjagfuieruisdcjbvhuidh";
var result = new Dictonary<char, int>();
foreach(var c in inputString)
{
if(result.ContainsKey(c))
result[c]++;
else
result.Add(c, 1);
}
foreach(var c in result)
{
Console.WriteLine($"{c.Key} = {c.Value}");
}
Это хороший вариант, достаточно быстрый, но очень вероятно, что от вас хотят увидеть не это, т.к. я хорошо знаком со спецификой обучения информатике в наших школах.
Тогда можно использовать решение совсем "в лоб", которое, к тому же, будет еще и на порядок быстрее любых LiNQ, Dictionary и тем более RegExp-ов.
Вспоминаем, что char - это на самом деле int, в том смысле, что символы хранятся в виде их числовых кодов. Скорее всего, в вашей задаче есть ограничение на диапазон символов, но для простоты, возьмем все символы двухбайтового unicode, который используется в .NET
//Для 2х-байтового юникода это всего 2^16 возможных символов.
//Элементы массива по умолчанию инициализируются 0, в отличие от C/C++.
int[] counters = new int[65536];
var inputString = "hghjkgdjfbdgfjafsdjagfuieruisdcjbvhuidh";
for(int i = 0; i < inputString.Count(); i++)
{
counters[(int)inputString[i]]++;
}
for(int i = 0; i < 65536; i++)
{
if(counters[i] > 0)
Console.WriteLine($"{(char)i} = {counters[i]}");
}
Главная идея заключается в том, что у элемента массива уже есть два независимых значения: индекс элемента и его содержимое. На Паскале это выглядит более очевидно, т.к. в качестве индексов можно использовать символы. В Си-подобных языках индекс всегда число, поэтому нужно предварительно преобразовать char в int, а потом уже использовать его в качестве индекса.
Да, тут есть избыточность по используемой памяти, но она компенсируется скоростью доступа по индексу к массиву и отсутствием создания дополнительных объектов "за кадром", как в случае с LiNQ и RegExp. К тому же, набор входных символов, скорее всего, будет ограничен набором однобайтовых символов, в этом случае можно обрезать длину массива до 256, или ASCII-символов, в этом случае можно обрезать длину массива до 128. Также можно вычесть первые 32 непечатных символа, только не забудьте, что вычитать их нужно при каждом преобразовании char в int, т.к. индексация массива всегда начинается с 0.
Красивое короткое решение уже написал @Андрей, будет замечательно, если вы разберетесь как оно работает, только не вздумайте писать такое решение на ЕГЭ, проверяющие не поймут, а вам придется таскаться по апелляциям.
NullRefrenceException. – Dec 03 '17 at 18:14