Mассивы класса c шарп

285
26 ноября 2017, 11:48

Не могу справится с, на первый взгляд,простой задачей: В строке найти количество вхождений каждой буквы.Создал класс с двумя свойствами

class Struct1
{
    public  char Itsch { get; set; }
    public  int Itscon { get; set; } 
}

строку отсортировал перевел в 2 чаровских массива,в дном оставил только уникальные буквы и на его размере создал массив елементов класса

Struct1[] arr = new Struct1[size];

Теперь пытаюсь сделать так

int i = 0;
for (int j = 0; j <ch.Length -1; j++)
{
    arr[i] = new Struct();
    count = 1;  arr[i].Itsch = ch[j];
    while (ch[j] == ch[j + 1])
    {
        count++;
        j++;
    }
    arr[i].Itscon = сount;
    i++;
}

не получается! Пытаюсь выкрутиться двумя циклами

for (int i = 1; i < ch2.Length; i++)
{
    arr[i] = new Struct1();
    arr[i].Itsch = ch2[i];
}

а затем

for(int j=0;j<ch.Length;j++)
{
    if (arr[j].Itsch == ch[j])
    arr[j].Itscon++;
}

Выбивает исключение NullRefrences во втором цикле, но как инициализировать arr[j]??? Если опять arr[j] = new Struct1(); все обнулиться и (arr[j].Itsch == ch[j]) станет бесполезна. Люди умные, помогите несчастному кто чем может!

Answer 1

С помощью Linq задача решается в одну строку:

string text = "qwertyuiodfghj9ij6ygrd3es7hiprrreeuuuqqqhhhhqqkkk";
var res = text.GroupBy(c => c).Select(g => new { Symbol = g.Key, Count = g.Count() }).ToList();
foreach (var r in res)
    Console.WriteLine("Количество символов {0} = {1}", r.Symbol, r.Count);

Группируем по символу и берем количество элементов в группе

Answer 2

Идею решения вы уловили правильно, поэтому останавливаться на подробном алгоритме не стану. А вот реализовать эту идею можно многими способами, включая массу неэффективных во всех отношениях вариантов.

Никогда не торопитесь создавать новый класс. В 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.

Красивое короткое решение уже написал @Андрей, будет замечательно, если вы разберетесь как оно работает, только не вздумайте писать такое решение на ЕГЭ, проверяющие не поймут, а вам придется таскаться по апелляциям.

Answer 3

Всё на много проще, чем кажется на первый взгляд. Используем Linq, и RegularExpressions.

        static void Main(string[] args)
        {
            const string text = "qwertyuiodfghj9ij6ygrd3es7hiprrreeuuuqqqhhhhqqkkk";
            foreach (var letter in text.Distinct().ToArray())
            {
                var count = Regex.Matches(text, letter.ToString()).Count;
                Console.WriteLine("Количество символов {0} = {1}", letter, count);
            }
            Console.ReadKey();
        }

Answer 4

Думаю в конкретно вашем случае решить можно проще. Хорошо, что вы отсортировали строку, но зачем после этого включать дополнительный цикл внутрь. Не проще ли первый элемент создать перед циклом, а не делать это внутри. И после каждый последующий символ сравнивать с текущим в вашем результирующем массиве.

int i = 0;
arr[i] = new Struct() { Itsch = ch[0], Itscon = 1 };
for (int j = 1; j <ch.Length; j++)
{
    if (arr[i].Itsch == ch[j])
        arr[i].Itscon++;
    else
    {
        i++;
        arr[i] = new Struct() { Itsch = ch[j], Itscon = 1 };
    }
}

Это решение только для конкретно вашего инструментария. Использование лямбд, как показали выше, действительно упростит решение.

READ ALSO
Ошибка: SQL logic error near &ldquo;insert&rdquo;: syntax error"

Ошибка: SQL logic error near “insert”: syntax error"

Выдаёт ошибку: SQL logic error near "insert": syntax error" Вот код:

247
Имплементация Producer/Consumer pattern

Имплементация Producer/Consumer pattern

Паттерн producer/consumer достаточно часто встречается в многопоточном программированииЕго смысл состоит в том, что один или несколько потоков...

253
Заморозка одной оси вращения

Заморозка одной оси вращения

Интересует вопрос, как заморозить одну ось вращения, чтобы третья не меняласьНапример, если использовать две оси вращения (X и Y ), то объест...

210
C# алгоритм перебора паролей

C# алгоритм перебора паролей

Всем привет! По учебе нужно написать код (обязательно рекурсивный!), который бы по заданному слову перебирал все возможные пароли, полученные...

255