Zadanie ze SPOJa - zliczacz liter, bardziej optymalne rozwiązanie

0

Treść zadania: http://pl.spoj.com/problems/JZLICZ/

Rozwiązałem to zadanie poprawnie, jednak uważam, że moje rozwiązanie jest mocno przekombinowane, ale niestety nic lepszego nie udało mi sie wymyślić (co pewnie wynika ze słabej znajomości języka/bibliotek). Chciałbym prosić o podanie bardziej optymalnego rozwiązania tego zadania w c#.

using System;
using System.Collections.Generic;
using System.Linq;

namespace zad34_zliczacz_liter
{
    class Program
    {
        static void Main(string[] args)
        {
            List<litera> literyAlfabetycznie = new List<litera>();
            List<char> ciagLiterWejsciowych = new List<char>();
            List<litera> koncowaLista = new List<litera>();
            int iloscLiniDoWczytania = int.Parse(Console.ReadLine());

            //wszystkie litery z wejscia wrzucamy do jednego kontenera
            for (int i = 0; i < iloscLiniDoWczytania; i++)
            {
                var wczytaneSlowa = Console.ReadLine().Split(' ');

                foreach (var slowo in wczytaneSlowa)
                {
                    slowo.ToCharArray();
                    foreach (var s in slowo)
                    {
                        ciagLiterWejsciowych.Add(s);
                    }
                }
            }

            //tworzymy liste liter wraz z ilością ich wystąpień
            foreach (var l in ciagLiterWejsciowych)
            {
                if (!literyAlfabetycznie.Exists(n => n.nazwa == l))
                {
                    var iloscTakichSamychLiter = ciagLiterWejsciowych.Count(x => x == l);
                    literyAlfabetycznie.Add(new litera(l, iloscTakichSamychLiter));
                }

            }

            //sortujemy alfabetycznie
            literyAlfabetycznie.Sort((x,y) => string.Compare((x.nazwa).ToString(), (y.nazwa).ToString()));


            //do ostatecznej listy dodajemy najpierw male litery
            foreach (var item in literyAlfabetycznie)
            {
                if (Convert.ToInt32(item.nazwa) > 96)
                {
                    koncowaLista.Add(item);
                }
            }

            //nastepnie duze
            foreach (var item in literyAlfabetycznie)
            {
                if (Convert.ToInt32(item.nazwa) < 97)
                {
                    koncowaLista.Add(item);
                }
            }

            foreach (var l in koncowaLista)
            {
                l.wypisz();
            }
            Console.ReadKey();
        }

        public class litera
        {
            public char nazwa { get; set; }
            public int ilosc { get; set; }

            public litera(char n, int i)
            {
                this.nazwa = n;
                this.ilosc = i;
            }

            public void wypisz()
            {
                Console.WriteLine("{0} {1}", nazwa, ilosc);
            }
        }
    }
}
 
0

@ray1234 na pewno da się to stuknąć w 1 linijce za pomocą linq :) Jak taki @somekind będzie miał dobry dzień to może nawet pokaże ci jak ;]

0

Ja bym to zrobił tak. @somekind pewnie zrobił by prościej. :P

static void Main(string[] args)
{
   var iloscLini = Convert.ToInt16(Console.ReadLine());
   var tekst = String.Empty;
   for (int i = 0; i < iloscLini; i++)
   {
      tekst += Console.ReadLine();
   }

   var lista =
      tekst.Where(r => r != ' ')
         .GroupBy(r => r)
         .OrderBy(g => g.Key)
         .Select(g => new {Count = g.Count(), Value = g.Key});


   foreach (var kvp in lista.Where(x => x.Value >= 'a'))
   {
      Console.WriteLine("{0} {1}", kvp.Value, kvp.Count);
   }

   foreach (var kvp in lista.Where(x => x.Value < 'a'))
   {
      Console.WriteLine("{0} {1}", kvp.Value, kvp.Count);
   }
}
0

Na pewno jest bardziej * optymalne rozwiązanie, ale chcąc zminimalizować linijki (pomijąc użycie ForEach):

using System;
using System.Linq;
using System.Text;

 class Program
    {
        static void Main(string[] args)
        {
           StringBuilder inputString = new StringBuilder();

            int t;
            t = int.Parse(Console.ReadLine());
            while (t > 0)
            {
                inputString.Append(Console.ReadLine().Replace(" ", string.Empty)); 
                t--;
            }

            inputString.ToString().Where(x => Char.IsLower(x)).GroupBy(x => x).Select(x => new { Value = x.Key, Count = x.Count() }).OrderBy(x => x.Value).Concat(inputString.ToString().Where(x => char.IsUpper(x)).GroupBy(x => x).Select(x => new { Value = x.Key, Count = x.Count() }).OrderBy(x => x.Value)).ToList().ForEach(x => Console.WriteLine("{0} {1}", x.Value, x.Count));         
                  
        }
    }

 
0

Bardzo dziękuję za rozwiązania :)

4
mkr napisał(a):

Na pewno jest bardziej optymalne rozwiązanie, ale chcąc zminimalizować linijki (pomijąc użycie ForEach):

Na pewno nie ma bardziej optymalnego rozwiązania. Jak coś jest optymalne, to jest najlepsze, więc nie może być "bardziej najlepsze".

No i minimalizm polegający na napisaniu całego kodu w jednym wierszu, to jest WTF-code, a nie minimalizm.

0

Może się nie znam na C# bo nigdy nie kodziłem, ale chciałem sprawdzić jednolinijkowce(nie licząc zmiennych)

 
static int tab['z'+1];
int i = 0;
for(string tekst = Console.ReadLine(); tekst[i];tab[tekst[i]]++,i++) 

Coś w tym stylu by uszło ?

0

@somekind nie chodziło mi oczywiście o upchanie do jednej linijki a raczej o jakis elegancki zapis bez 100 pętli ;) Coś w stylu:

        Map<Character, Long> x = new BufferedReader(new InputStreamReader(System.in))
                .lines()
                .flatMap(line -> line.chars().mapToObj(i -> (char) i))
                .filter(Character::isLetter)
                .collect(Collectors.groupingBy(
                                letter -> letter,
                                Collectors.counting()
                        )
                );
0

@Shalom, ale Twój wynik będzie zgodny z kolejnością ASCII, więc nie spełni wymagań zadania.

Tak w ogóle, to nie rozumiem po co jest to N w zdaniu, no ale ja chyba nigdy nie rozumiałem SPOJa. :P

No i niby można się bawić w pisanie jakichś super krótkich kawałków kodu, jak np. ten:

Console.ReadLine();
var result = Console.In.ReadToEnd().Where(char.IsLetter)
    .GroupBy(c => c)
    .OrderBy(c => c.Key, Comparer<char>.Create((a, b) => (char.IsUpper(a) ? a + 'A' : a) - (char.IsUpper(b) ? b + 'A' : b)))
    .ToDictionary(x => x.Key, x => x.Count());

foreach (var kvp in result)
{
    Console.WriteLine("{0} {1}", kvp.Key, kvp.Value);
}

Ale po co, skoro SPOJ i tak tego nie skompiluje? :)

0

Następny mózg o_O. HashMap nie ma ustalonej kolejności więc nie rozumiem twierdzenia ze kolejność będzie zła. Kolejność będzie taka jaka ktoś sobie tu zrobi przy budowaniu wyjścia :-)
A co do N to są języki w których ułatwia to pisanie :-P

0

Z treści zadania:

Output

W kolejnych wierszach litery od 'a' do 'z' i od 'A' do 'Z' w tej kolejności, a po każdej literze spacja i liczba wskazująca, ile razy ta litera wystąpiła w tekście.

Uwaga: pomiń litery, które nie występują w tekście.

A zatem, jeśli Twój kod nie daje takiego wyniku, to kolejność jest zła. Jeśli wolisz ją ustalić przy generowaniu wyjścia to ok, ale jak widzisz mój kod robi to już na etapie budowania mapy. A jestem po prostu ciekaw, jakby to wyglądało w Javie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1