Zliczanie znaków w stringu

0

Witam, chciałbym napisać funkcję która pozwala mi zliczyć powtarzalność znaków w danym stringu, nie mam jednak pomysłu jakiej komendy użyć przykładowo chciałbym aby po wpisaniu łańcucha wyświetlało mi np. Ala ma kota
A 1
l 1
a 3
m 1
a 3
k 1
o 1
t 1
a 3
Nie proszę o gotowe napisanie tylko o wskazówki z czego mam skorzystać. Pozdrawiam :)

3

skorzystaj z std::map
jako Key daj literke
jako value ile razy wystepuje w stringu

0

Skorzystaj z tego samego co masz w tytule wątku -> ze zliczania. Popatrz np. na algorytm sortowania przez zliczanie, bo masz zastosować bardzo podobny schemat.

1

Zwróciliście uwagę na przykładowe wyjście? Jeśli literka a wystąpiła trzy razy, to ma zostać trzy razy wypisana i do tego we właściwych wierszach.

1
#include <map>
#include <string>
#include <iterator>
#include <iostream>
#include <boost/range/algorithm/copy.hpp>
#include <boost/function_output_iterator.hpp>
using namespace std;
int main(){
    string str{
        istream_iterator<char>(cin), 
        istream_iterator<char>()
    };

    map<char, size_t> occurs;
    boost::copy(str, boost::make_function_output_iterator([&](char c){ occurs[c] += 1; }));
    
    for(char c : str){
        cout << c << ": " << occurs[c] << "\n";
    }
}

http://melpon.org/wandbox/permlink/d0FmsSyWNBuoLR8g

Nie proszę o gotowe napisanie

To dobrze, bo byś nie dostał :P

0

Znaczy powiem wprost no jest to zadanie na studia, i widzę że te funkcje i konstrukcje wybiegają daleko poza zakres mojej wiedzy więc wydaje mi się że powinien być jakiś prostszy sposób.

2

dobrze, nie chcesz gotowych funkcji to podam Ci liste krokow ktore musisz zrobic. Zajmie Ci pisanie pewnie z pare godzin zamiast napisanie tego w ciagu godziny i zrozumieniu tego. Ale jezeli lubisz wymyslac kolo na nowo to nikt Ci nie bedzie tego zabranial

  1. Robisz tablice z kazdym znakiem ktory chcesz by byl zliczany
    0.1 Robisz druga tablice pusta o typie int (w sensie ma wszystko na zero) o wielkosci tablicy pierwszej
  2. Wczytujesz linie tekstu (szukaj pod getline)
  3. Iterujesz po tym slowie jako kazdy znak
    2.1. szukasz w tablicy znakow obecnego znaku
    2.2 zapisujesz indeks w tablicy
    2.3 w drugiej tablicy o tym samym indeksie zwiekszasz liczbe o 1
  4. Wpisujesz tablice pierwsza i druga w tym samym momencie
0

Stworzyłem więc tablice z każdym znakiem który chce żeby był wczytywany, czyli praktycznie każdy z klawiatury. Stworzyłem tą tablice int i co dalej krok 1 i 2 również, jednak nie wiem jak mam "wyszukać" czy dany znak wystąpił.

2

A poświęciłeś 2 minuty na przeczytanie jak działa sortowanie przez zliczanie? Nie? To poświęć.

1

Tablica kodów dla znaków ASCII to od 127 do 256 liczb, stąd mieszczą się w char(8 bit) w zasadzie niezależnie od kodowania pozostałych znaków normalne litery i liczby to zakres od ok 48 do 127, przykładowo spacja to liczba 32, cyfra 0 to kod 48, litera "A" to liczba 65, "B" to 66 itd. małe "a" to 97 bo 65 + 32 = 97 (analogicznie dla wszystkich liter)

Zatem potrzebna jest zaledwie jedna dodatkowa tablica o rozmiarze 256 bajtów indeksowana znakami.
Rodzaj zmiennej użytej w tablicy trzeba dobrać do długości tekstu więc dla długości 256 znaków wystarczy unsigned __int8 dla tekstów o rozmiarze 4GB unsigned __int32 więc zaledwie od 256 do 1024 bajtów RAM. Zerujesz tablice liczebnosci[256] (przejście od 0 do 255). W przejściu petli for (i=0; i<znaki.length();i++) dla tekstu inkrementujesz liczebnosc[(unsigned char)znak[i]]++; i już masz liczebności. Dla wydrukowania potrzebne jest kolejne przejście podobnej petli (znowu od zera do znaki.length()): cout << (char)znak[i] << liczebnosc[(unsigned char)znak[i]] << endl;
Koniec.

Nie wiem co jest trudnego do zrozumienia, wystarczy zakodować poprzez kopiuj wklej dwóch petli, wczytać znaki z klawiatury poprzez getch() albo scanf i już.
Jak słusznie zauważył Shalom można to uznać za fragment algorytmu sortowanie przez zliczanie.

@fasadin - dokładnie tak jak napisałem potrzebna jest jedna tablica bo tej która zawiera tekst nie wliczam.
@spartanPAGE podał uniwersalną wersję algortymu która zadziała jesli będzie musiala zliczać np. sylaby, wyrazy, frazy większe elementy którym nie kody ASCII ale map przyporządkuje unikatowe liczby.

1

@wojtaaz to jest taki trik w zliczaniu, że indeksujemy tablicę znakami które zliczamy. Np. litera a ma numer 48 w ascii. Więc tablica['a'] to jest po prostu tablica[48]. Jeśli teraz cała tablica jest wyzerowana i zrobimy tablica['a']++ to 48 indeks w tablicy będzie miał 1.
Więc teraz jeśli iterujemy sobie po literkach w ciągu wejściowym np. alamakota gdzie literki[i] to jest kolejna literka z ciągu i wykonujemy tablica[literki[i]]++ to w rzeczywistości robimy:
i=0: tablica[literki[i]]++ -> tablica['a']++ -> tablica[48]++
i=1: tablica[literki[i]]++ -> tablica['l']++ -> ...

Więc w tablicy dla indeksu odpowiadającego danej literce będziesz miał informacje o tym ile razy literka wystąpiła.

1

No, to żeby nie proponować tego co zaproponowali koledzy... a rzecz jasna zaproponowali optymalne obliczeniowo rozwiązanie, to podam inne które oszczędza RAM ale kosztem złożoności obliczeniowej.
Podkreślam jeszcze raz, obliczeniowo gorsze, pamięciowo o lepsze bo wymaga jedynie pamięci RAM o długości string'a. A pytający nie zdradził "co mieli a co nie na zajęciach", więc bez użycia jakichkolwiek bardziej zaawansowanych struktur i w C (żeby zaraz nie było "chcę bez <algorithm>").

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/*
 * Zlicza wystąpienia znaków w całym napisie pod indeksem danego znaku występującego jako pierwszy
 * z lewej strony.
 */
void count_chars_cons(const char * str, size_t * table) {
    size_t i;
    const char * start_str = str;

    while(*str != '\0') {
        i = 0;
        while(*str != start_str[i]) {
            ++i;
        }
        ++table[i];
        ++str;
    }
}

/*
 * Wyświetla ilość wystąpnień dla danej litery.
 */
void show_chars_count(const char * str, size_t * table) {
    char c;
    size_t count;

    while((count = *(table++), c = *(str++)) != '\0') {
        if(count > 0) {
            printf("%c: %zu\n", c, count);
        }
    }
}

int main(void) {
    const char str[] = "Ala ma kota i chomika. Nic z tego nie wynika.";
    const size_t str_len = sizeof(str) - 1;
    size_t count[sizeof(str) - 1];

    bzero(count, str_len * sizeof(size_t));
    count_chars_cons(str, count);
    show_chars_count(str, count);

    return EXIT_SUCCESS;
}
0

liczebnosc [256] = 0;
for (int i=0; i<sizeof(znaki); i++)
liczebnosc[(const char)znaki[i]]++;
for(int i=0; i<sizeof(znaki); i++)
cout << (char)znak[i] << liczebnosc[(const char)znaki[i]] << endl;

Milion warnów

|In function 'void CountChar(const char*, int*)':|
warning: comparison between signed and unsigned integer expressions [-Wsign-compare]|
warning: array subscript has type 'char' [-Wchar-subscripts]|
warning: comparison between signed and unsigned integer expressions [-Wsign-compare]|
warning: array subscript has type 'char' [-Wchar-subscripts]|

Wiem że prawdopodobnie to są banały, jednak nie mogę pojąć tego co robie źle.

0

W więc od dziś zdanie:

Nie proszę o gotowe napisanie tylko o wskazówki z czego mam skorzystać. Pozdrawiam :)

Będę odczytywać: "Wiem że regulamin zabrania ogłoszeń z prośbą rozwiazania zadania domowego, dlatego sformułowałem inaczej :)"

Wiem że prawdopodobnie to są banały, jednak nie mogę pojąć tego co robie źle.

  1. Zmiast unsigned char przepisałeś const char!? Rozumiesz co oznacza unsigned?
    Dla polskich znaków, przy ustawieniu strony kodowej setlocale(LC_CTYPE, ".1250"); np. dla liter ąę jako char da wynik ujemny słusznie kompilator ostrzega przed ryzykiem zaindeksowania tablicy ujemnymi liczbami.
  2. Masz char* jako parametr funkcji i rzecz jasna sizeof(char) zamiast np. strlen(znaki) odpowiednik podanego przeze mnie C++ string.length() - na co wyraźnie wskazuje tytuł tematu! Notabene optymalną formą pętli jest konstrukcja for (int i = 0, max = strlen(znaki); i < max; i++); bo strlen wystarczy by został wywołany raz, aby sprawdził pozycje znak kończącego tekst! tj. o wartości liczbowej zero '\0'. Natomiast dla char* znaki; sizeof(znaki) zwróci rozmiar wskaźnika: 4 albo 8 bajtów. Nawet gdyby był to char znaki[100]; sizeof(znaki) nie zwróci rozmiaru tekstu, ale rozmiar kontenera.
  3. Zerujesz jeden element listy w dodatku wykraczający poza jej rozmiar... rozumiesz w ogóle co piszesz?
    Zerowanie można uzyskać tworząc ją przy pomocy __int8 liczebnosc = (__int8)calloc(256, sizeof(__int8)); lub jesli jest na stosie __int8 liczebnosc[256]; poprzez: for(int i = 0; i < 256; i++) liczebnosc[i] = 0;

Zacznij od przeczytania dobrej "fabularyzowanej" książki na temat C będzie i Tobie i innym znacznie lżej.
Zacznij od BASIC'a albo innego języka, znacznie przyjemniej mniejsze problemy.

0

Rozumiem że pytania są dość hmm banalne i problemy tak samo. Wiem przepraszam za pomyłke w samym tytule. Na prawdę starałem się to zrobić jakoś zamodzielnie i zrozumieć najlepiej jak moge natomiast po prostu mam z tym problem, i nie ukrywam że sprawa nagli.

0

to jak sprawa nagli to zaplac komus zeby to za Ciebie zrobil

0

Nie chodziło mi o zrobienie tego za mnie, tylko po prostu wytłumaczenie jak kretynowi jak to powinno wyglądać, co zamiast czego... Mam ewidentny problem z tym, co rzecz jasna nie sposób nie zauważyć, jednak na chwile obecną nie mam aż tyle czasu aby to zrobić, bo widze że przedmną sporo pracy. Po prostu zabierając siędo pisania tego myślałem że to bęzie prostrze tj. do rozwiązania jakąś konkretną funkcją jednak widze że konstrukcje takich funkcji wykraczają poza mój materiał.

0

nikt nie bedzie Ci tlumaczyl to co jest w setkach ksiazkach i kursach w internecie. Po prostu wez ksiazke i sie poucz troche

0

Rozumiem że "liczebnosc" ma być zapisana jako parametr funkcji w postacii unsigned char tak?

0

W każdym razie został mi jeden błąd do wykluczenia symbol gwiazdki ma związek coś ze wskaźnikami którymi jeszcze nie operuje i pytanie jak mam to rozwiazac.

void CountChar(string symbols, int SizeOfS [256])
{
for(int i = 0; i < 256; i++) SizeOfS[i]=0;
for(int i = 0; i< symbols.length(); i++)
SizeOfS[(unsigned char)symbols[i]];
for (int i = 0; i<symbols.length(); i++);
int main()
{ char myString[256]; int myStrLen;
CountChar(myString, myStrLen);
error: invalid conversion from 'int' to 'int*' [-fpermissive]|

2

To co napisałeś nie ma sensu. Najpierw masz char myString[256] a w funkcji próbujesz go upychać jako string symbols a potem jeszcze lepiej masz int myStrLen a w funkcji chcesz z tego zrobić int SizeOfS[256]. Cudowne rozmnożenie? Czy może po prostu nie rozumiesz ani linijki z tego kodu który wkleiłeś?

0

Stringa zasugerowano mi wyzej, chcialem wiedziec wlasnie jakiego typu argumenty mam uzyc jako parametry funkcji, pytalem o to zasugerowano mi ze przeciez nie mam stringa tylko chara, no to zmienilem. Chcialbym wiedziec jakiego typu parametry powinienem zadeklarowac.

0

Mam dość tematu ale lubie sprawy zamykać do końca.
Podaje rozwiazanie które działa o funkcjonalność która nie była oczekiwana tj. brak powtórzeń w drukowaniu listy. Może jesteś prowokatorem który robi sobie żarty z ludzi a może jedynie skutecznym w uzyskiwaniu odpowiedzi bez wkładu własnego. Kodowanie na chybił trafil przechodzi ludzkie pojecie.. Alphabet testuje algorytm AI? Tłumaczysz znaki na symbols okej masz prawo, zamiast zastosować strlen zmieniasz char* na string, zamiast liczebność SizeOfS jak w czeskim filmie.

#include <iostream>
#include <cstring>
#include <cstdio>
#include <conio>
//#include <windows>
using namespace std;

 int EnterChars(char*, const int);
void CountChars(const char*, const int, unsigned int[]);
void PrintCharsAndCounts(const char*, const int, const unsigned int[]);

#define MAX_LENGTH 256
int main()
{
printf("Program do obliczania statystyk wystepowania znaków w tekście v1.0\n");
//SetConsoleCP(1251); 
setlocale(LC_ALL,"pl_PL.ISO-8859-2");
system("chcp 1250");
char Chars[MAX_LENGTH];
 unsigned int CharsCounts[256];
 int CharsLen = EnterChars(Chars,MAX_LENGTH);
CountChars(Chars,CharsLen, CharsCounts);
PrintCharsAndCounts(Chars,CharsLen, CharsCounts);
printf("Wciśnij klawisz aby kontynuować . . .\n");
getch();
//system("pause");
return 0;
}

//--------------------------------------------------
// Wprowadzenie tekstu
//--------------------------------------------------

int EnterChars(char *a_chars, const int a_max_len)
{
printf("Wprowadź tekst do analizy i naciśnij ENTER\n");

/*int i;
   for (i = 0; i < MAX_LENGTH) 
    {
    a_chars[i] = getch();
    if (a_chars[i]=='\r') break; 
    }
   a_chars[i]='\0'; return i;
*/
std::scanf("%s",a_chars);
return std::strlen(a_chars);
}
//--------------------------------------------------
// Zliczanie liczebności znaków
//--------------------------------------------------

void CountChars(const char *a_chars, const int a_chars_len, unsigned int a_chars_counts[])
{
    for(int i = 0; i < 256; i++)
		{
		a_chars_counts[i] = 0;
		}
    for(int i = 0; i < a_chars_len; i++)
    	{
		a_chars_counts[(unsigned char)a_chars[i]]++;
    	}
}

//--------------------------------------------------
// Drukowanie znaków i liczebności
//--------------------------------------------------

void PrintCharsAndCounts(const char *a_chars, const int a_chars_len, const unsigned int a_chars_counts[])
{
std::printf("Liczebność znakow w tekście:\n");
 int l_unique_chars_count = 0;
bool l_char_printed[256];
for (int i = 0; i < 256; i++)
	{
    l_char_printed[i] = false;
    }
for (int i = 0; i < a_chars_len; i++)
	{
     if (l_char_printed[(unsigned char)a_chars[i]]==false)
        {
        std::printf("%c - %d\n", a_chars[i], a_chars_counts[(unsigned char)a_chars[i]]);
        l_char_printed[(unsigned char)a_chars[i]] = true;
        l_unique_chars_count++;
        }
	}
std::printf("Ilość wszystkich znakow: %d\n",a_chars_len);
std::printf("Ilość różnych znakow: %d\n", l_unique_chars_count);
std::printf("Koniec!\n");
}

Przy okazji zauważylem że setlocale nie działa jak powinno niezaleznie od wybranego kodowania.
Możliwe że to rozbieżność w edytorze i wprowadzanych z klawiatury.

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