Problem z zadaniem maturalnym 2010 c++

0
#include <iostream>
#include <fstream>
#include <cstring>

using namespace std;
int sprawdzDlugosc(int dl[])
{
    for (int i=0; i<4; i++)
        if(dl[i]!=dl[i+1]) return 0;
    return 1;
}
bool Czyanagram (char *a, char *b)
{
    int dl1 = strlen(a), dl2 = strlen(b);
    if(dl1!=dl2) return false;
    int licznik[201]={0};
    for (int i=0; i<dl1; i++)
        licznik[a[i]]++;
    for (int i=0; i<dl1; i++)
        licznik[b[i]]--;
    for (int i=0; i<201; i++)
        if(licznik[i]!=0) return 0;
    return 1;

}

main()
{
    ifstream fin("anagram.txt");
    ofstream fout1("odp_4a.txt");
    ofstream fout2("odp_4b.txt");
    string s[6];
    int dl[6];
    char a[100], b[100];
    int i;
    while(!fin.eof())
    {
        for(int i=0; i<5; i++)
        {
            fin>>s[i];
            dl[i]=s[i].size();
        }
        if(sprawdzDlugosc(dl))
        {
            for(int i=0; i<5; i++) fout1<<s[i]<<" ";
            fout1<<endl;
        }

    }
    while(!fin.eof())
    {
      fin>>s[i];
      if(Czyanagram(a,b)) fout2<<s[i]<<" ";
      fout2<<endl;
    }
    fin.close();
    fout1.close();
    fout2.close()
    return 0;
}

Witam.
Mam problem z tym zadaniem z podpunktem b). Znam kod na szukanie anagramów ale dla dwóch wyrazów *a i *b a nie wiem jak ten kod przerobić, żeby przeszukiwał wszystkie słowa z pliku tekstowego i wyznaczał je. Proszę o pomoc.
Ps .Nie wiem czy dobrze wkleiłem kod bo to mój 3 post tutaj

Zrzut ekranu 2021-04-25 175925.png

1

W prawym dolnym rogu masz krótki opis, jak formatować kod, oraz możesz wybrać w menu, jaki język chcesz przekleić.
Twój kod po sformatowaniu wygląda tak:

#include <cstring>
#include <fstream>
#include <iostream>

using namespace std;
int sprawdzDlugosc(int dl[]) {
    for (int i = 0; i < 4; i++)
        if (dl[i] != dl[i + 1]) return 0;
    return 1;
}
bool Czyanagram(char a, char b) {
    int dl1 = strlen(a), dl2 = strlen(b);
    if (dl1 != dl2) return false;
    int licznik[201] = {0};
    for (int i = 0; i < dl1; i++) licznik[a[i]]++;
    for (int i = 0; i < dl1; i++) licznik[b[i]]--;
    for (int i = 0; i < 201; i++)
        if (licznik[i] != 0) return 0;
    return 1;
}

main() {
    ifstream fin("anagram.txt");
    ofstream fout1("odp_4a.txt");
    ofstream fout2("odp_4b.txt");
    string s[6];
    int dl[6];
    char a[100], b[100];
    int i;
    while (!fin.eof()) {
        for (int i = 0; i < 5; i++) {
            fin >> s[i];
            dl[i] = s[i].size();
        }
        if (sprawdzDlugosc(dl)) {
            for (int i = 0; i < 5; i++) fout1 << s[i] << " ";
            fout1 << endl;
        }
    }
    while (!fin.eof()) {
        fin >> s[i];
        if (Czyanagram(a, b)) fout2 << s[i] << " ";
        fout2 << endl;
    }
    fin.close();
    fout1.close();
    fout2.close()
    return 0;
}

Ten kod się nawet nie kompiluje. Popraw najpierw błędy, żebyśmy mogli się skupić na istocie problemu zamiast technicznych szczegółach składni C++.

3
  1. Popraw kod, bo się nie kompiluje.
  2. Kod można poprawić, zamiast char * warto użyć std::string, zamiast int licznik[201] użyć std::unordered_map<char, int>.
  3. Jakie jest twoje zrozumienie kodu na anagramy? Nauczenie się go na pamięć to nie jest dobry pomysł.
    Twój kod korzysta z faktu, że dwa słowa są anagramami jeżeli mają takie same częstości występowania liter.
    Znając ten fakt, można napisać funkcję, która przyjmuje licznik z częstością występowania liter (rozkład) w pierwszym słowie i sprawdzić czy ów rozkład jest identyczny w drugim słowie.
  4. Samo sprawdzanie czy dwa słowa są anagramami można zaimplementować inaczej. Zastanów się co się stanie, jeżeli posortujesz dwa słowa, które są anagramami i porównasz (zakładam kodowanie ASCII, żadnych utf-8).
2

Straszny potworek!

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <algorithm>
using namespace std;

int main()
{   
    ifstream fin("anagram.txt");
    ofstream afout("odp_4a.txt"),bfout("odp_4b.txt");
    for(string line,first,second;getline(fin,line);)
    {
    	istringstream sin(line);
    	sin>>first;
    	sort(begin(first),end(first));
    	bool a=true,b=true;
    	while((sin>>second)&&(a)&&(b))
    	{
    		if(first.size()==second.size())
    		{
	    		sort(begin(second),end(second));
    			if(first!=second) b=false;
    		}
    		else a=b=false;
    	}
    	if(a) afout<<line<<endl;
    	if(b) bfout<<line<<endl;
    }
    return 0;
}
4

@Jakub Wrona: Warto wykorzystać funkcję std::is_permutation

0
#include <cstring>
#include <fstream>
#include <iostream>

using namespace std;
int sprawdzDlugosc(int dl[]) {
    for (int i = 0; i < 4; i++)
        if (dl[i] != dl[i + 1]) return 0;
    return 1;
}
bool Czyanagram(char *a, char *b) {
    int dl1 = strlen(a), dl2 = strlen(b);
    if (dl1 != dl2) return false;
    int licznik[201] = {0};
    for (int i = 0; i < dl1; i++) licznik[a[i]]++;
    for (int i = 0; i < dl1; i++) licznik[b[i]]--;
    for (int i = 0; i < 201; i++)
        if (licznik[i] != 0) return 0;
    return 1;
}

main() {
    ifstream fin("anagram.txt");
    ofstream fout1("odp_4a.txt");
    ofstream fout2("odp_4b.txt");
    string s[6];
    int dl[6];
    char a[100], b[100];
    int i;
    while (!fin.eof()) {
        for (int i = 0; i < 5; i++) {
            fin >> s[i];
            dl[i] = s[i].size();
        }
        if (sprawdzDlugosc(dl)) {
            for (int i = 0; i < 5; i++) fout1 << s[i] << " ";
            fout1 << endl;
        }
    }
    while (!fin.eof()) {
        fin >> s[i];
        if (Czyanagram(a, b)) fout2 << s[i] << " ";
        fout2 << endl;
    }
    fin.close();
    fout1.close();
    fout2.close();
    return 0;
}

poprawiony kod.

0

@_13th_Dragon: Hmm. Nie wiem na jakim poziomie jest ten kod napisany ale jestem w 2 liceum i na razie umiem tyle ile nauczyłem się w szkole (może trochę więcej). Nie rozumiem i nie znam wielu tych komend. Myślałem bardziej na zastosowaniu jakiś pętli "for" czy coś takiego.

3
Jakub Wrona napisał(a):

@_13th_Dragon: Hmm. Nie wiem na jakim poziomie jest ten kod napisany ale jestem w 2 liceum i na razie umiem tyle ile nauczyłem się w szkole (może trochę więcej). Nie rozumiem i nie znam wielu tych komend. Myślałem bardziej na zastosowaniu jakiś pętli "for" czy coś takiego.

@Jakub Wrona:

  • Czy jesteś w 2 kasie po g. czy po p.? Bo to są dwie różne matury.

  • Jeśli wybierasz język na maturę, to zdecydowanie Python -- na maturze chodzi o to, żeby jednorazowo zrobić coś szybko i żeby działało. Nie będziesz tego kodu pielęgnował (więc statyczne typowanie możesz sobie darować) i nikt nie będzie wymagał szybkiego działania (więc i tu C++ Ci niepotrzebne). Do matury masz jeszcze rok (dwa, jeśli jesteś po p.), przez ten czas możesz Pythona wyuczyć się na poziomie dużo wyższym niż wystarczający do matury.

  • Jeśli bardzo chcesz pisać w C++, to na maturze pamiętaj, że możesz korzystać ze wszystkiego, co jest w standardzie języka. Nie ma co startować do matury w C++ bez BIEGŁEJ znajomości tak podstawowych rzeczy (z C++11) jak (kolejność przypadkowa): vector, map, set, string, deque, for z dwukropkiem, stringstream, sort -- a (z przyczyn dla mnie nieodgadnionych) tego jakoś zwykle nauczyciele nie chcą uczyć w liceum/technikum...

2
koszalek-opalek napisał(a):

Nie ma co startować do matury w C++ bez BIEGŁEJ znajomości tak podstawowych rzeczy (z C++11) jak (kolejność przypadkowa): vector, map, set, string, deque, for z dwukropkiem, stringstream, sort -- a (z przyczyn dla mnie nieodgadnionych) tego jakoś zwykle nauczyciele nie chcą uczyć w liceum/technikum...

Popieram, co do całego postu, jedynie chcę wytłumaczyć "...z przyczyn dla mnie nieodgadnionych ..."
Otóż prześwieca im idea ze jak nauczą studentów pisać takie rzeczy to nauczenie się samodzielnie np map to kwestia kilku minut.
Z tym że za każdym razem 90% nie daje się nauczyć samodzielnego pisania nawet mocno uproszczonej klasy vector,
zaś pozostałe 10% pozostają na poziomie uproszczonej klasy vector.

0

@koszalek-opalek: Jestem w 2 liceum po gimnazjum. Czyli rozumiem, że z tymi rzeczami co uczą do matury w szkole (w języku C++) to nie mam szans zdać dobrze matury i muszę jak najszybciej zacząć uczyć się Pythona?

3

Masz jako-taką szansę zdać dobrze maturę tym, co zazwyczaj uczą w szkołach. Jeśli chcesz te swoje szanse poprawić, to pozostaje Ci albo opanować C++ lepiej niż się zazwyczaj naucza, albo nauczyć się samodzielnie Pythona — przy czym to drugie wymaga zazwyczaj mniej wysiłku.

1
Jakub Wrona napisał(a):

@koszalek-opalek: Jestem w 2 liceum po gimnazjum. Czyli rozumiem, że z tymi rzeczami co uczą do matury w szkole (w języku C++) to nie mam szans zdać dobrze matury i muszę jak najszybciej zacząć uczyć się Pythona?

Zgadzam się właściwie z komentarzem @Cepo -- a bardziej szczegółowo: szanse w C++ masz, i bez standardowych kontenerów. Ale będzie to trudne. W C++ z tym, co napisałem 3 posty wyżej, masz bardzo duże szanse -- więc jak C++ Ci leży ucz się dalej i będzie ok. Nie wiem, jakiego masz nauczyciela (jest trochę bardzo dobrych), ale nawet z najlepszym, dużo samodzielnej pracy przed Tobą. A jak już uczyć się samodzielnie, to Pythona. :)

PS. Ale jak masz sensownego nauczyciela to możesz trzymać się C++.

0
#include <cstring>
#include <fstream>
#include <iostream>

using namespace std;
int sprawdzDlugosc(int dl[])
{
    for (int i = 0; i < 4; i++)
        if (dl[i] != dl[i + 1]) return 0;
    return 1;
}
int sprawdzZnak(char znak, string s)
{
    for(int i=0; i<s.size(); i++)
        if(s[i]==znak) return 1;
    return 0;
}
int sprawdzAnagram(string s[])
{
    for(int j=1; j<5; j++)
        for(int i=0; i<s[0].size(); i++)
            for(int k=0; k<s[j].size(); k++)
                if(!sprawdzZnak(s[j][k],s[0])) return 0;
    return 1;
}

main()
{
    ifstream fin("anagram.txt");
    ofstream fout1("odp_4a.txt");
    ofstream fout2("odp_4b.txt");
    string s[6];
    int dl[6];
    char znak[100];
    int i;
    while (!fin.eof())
    {
         for (int i = 0; i < 5; i++)
        {
            fin >> s[i];
            dl[i] = s[i].size();
        }

        if(sprawdzDlugosc(dl))
        {
            for (int i = 0; i < 5; i++) fout1 << s[i] << " ";
            fout1 << endl;
        }
        for (int i = 0; i < 5; i++)
        {
            fin >> s[i];
            dl[i] = s[i].size();
        }

        if(sprawdzDlugosc(dl))
            if(sprawdzAnagram(s))
        {
            for (int i = 0; i < 5; i++) fout2 << s[i] << " ";
            fout2 << endl;
        }
    }

    fin.close();
    fout1.close();
    fout2.close();
    return 0;
}

Ok. Nauczyciel wysłał nam jak ten kod ma wyglądać. Czy byłby ktoś na tyle uprzejmy i wytłumaczyłby mi w komentarzach w kodzie jak działa i na czym polega funkcja "sprawdzAnagram" bo za nic nie mogę tego zrozumieć.

0

@Jakub Wrona: Niczego nie tracisz, nie warto tego rozumieć.

2

Te dwie funkcje powinny wyglądać inaczej.

Źle:

int sprawdzDlugosc(int dl[])
{
    for (int i = 0; i < 4; i++)
        if (dl[i] != dl[i + 1]) return 0;
    return 1;
}
int sprawdzZnak(char znak, string s)
{
    for(int i=0; i<s.size(); i++)
        if(s[i]==znak) return 1;
    return 0;
}

Dobrze (a w każdym razie lepiej, proszę się nie czepiać o brak wektorów):

bool check_all_strings_equal_size(string tab[], size_t size) {
    for (size_t i = 0; i + 1 < size; ++i) {
        if (tab[i].size() != tab[i+1].size()) {
            return false;
        }
    }
    return true;
}
bool string_contains_char(string& s, char c) {
    return s.find(c) != string::npos;
}

Wtedy tablica dl wylatuje, i można uprościć maina (który notabene powinien zwracać inta).
Oraz dodatkowo, tablica string s[6] powinna być tak naprawdę tablicą string s[5]; (nauczyciel nie umie liczyć). Tablica char znak[100]; też nigdzie nie jest używana, można ją pominąć. Tablicę string s[5]; Można przenieść do środka pętli, zgodnie z maksymą, że deklaracja zmiennych powinna być jak najbliżej miejsca użycia (a nie ma potrzeby, żeby różne obroty tej samej pętli używały tych samych stringów).
Dodatkowo program jest błędny, bo w każdym obejściu pętli dwukrotnie czyta linijkę z pliku, a więc połowę linijek pomija w przypadku podpunktu a, i drugą połowę z nich pomija w przypadku podpunktu b.
Posprzątany main:

int main()
{
    ifstream fin("anagram.txt");
    ofstream fout1("odp_4a.txt");
    ofstream fout2("odp_4b.txt");

    while (!fin.eof()) {
        const size_t tab_size = 5;
        string s[tab_size];
        for (int i = 0; i < tab_size; i++) {
            fin >> s[i];
        }

        if (check_all_strings_equal_size(s, tab_size)) {
            for (int i = 0; i < tab_size; i++) {
                fout1 << s[i] << " ";
            }
            fout1 << endl;

            if (sprawdzAnagram(s)) {
                for (int i = 0; i < tab_size; i++) {
                    fout2 << s[i] << " ";
                }
                fout2 << endl;
            }
        }
    }

    fin.close();
    fout1.close();
    fout2.close();
    return 0;
}

A co do ostatniej funkcji, trochę żałosna jest, kwadratowa złożoność. No ale niech będzie.
Sama funkcja w sobie jest błędna, bo ignoruje opcję, że znaki w stringach mogą się powtarzać, i wtedy może zgłaszać błędne wyniki. Np. według niej, anagramami są "aaabb" oraz "babbb", ale ciężko powiedzieć czy tak miało być, czy tak przypadkiem wyszło.
Zmienna i nie jest używana w ogóle. W efekcie, funkcja działa kilka razy dłużej, robiąc dokładnie to samo. Równie dobrze można pominąć ją. Innymi słowy, to jest równoważne (o ile s[0].size() > 0):

int sprawdzAnagram(string s[])
{
    for(int j=1; j<5; j++)
        for(int k=0; k<s[j].size(); k++)
            if(!sprawdzZnak(s[j][k],s[0])) return 0;
    return 1;
}

No ale chyba stać nas na trochę więcej niż taką drobną poprawkę.

Trochę przepisana wersja. Wprowadzam funkcję, która sprawdza, czy pewne dwa stringi są anagramem.

bool is_anagram(string& s1, string& s2) {
    for (size_t k = 0; k < s1.size(); k++) {
        // Jeśli się zdaży, że s2 nie zawiera któregoś ze znaków z s1, to nie są anagramem.
        if (!string_contains_char(s2, s1[k])) {
            return false;
        }
    }
    return true;
}
bool sprawdzAnagram(string s[], size_t size)
{
    for(int k = 1; j < size; j++) {
        if (!is_anagram(s[0], s[k])) {
            return false;
        }
    }
    return true;
}
0

@Jakub Wrona: To rozwiązanie jest niepoprawne. "ala" i "all" wg tego rozwiązania są anagrammi, podczas gdy jest to ewidentnie nieprawda.

Pomijam karygodny styl i masę błędów.

1

@Jakub Wrona:

Można tak:

constexpr size_t num_lowercase_letters = 'z' - 'a' + 1;
using lowercase_counter = std::array<size_t, num_lowercase_letters>;

lowercase_counter count_lowercase(const string &word)
{
    lowercase_counter counter{};
    for (auto letter : word) {
        counter.at(letter - 'a')++;
    }
    return counter;
}

bool are_anagrams(vector<string> &words)
{
    auto counter = count_lowercase(words[0]);
    for (int i = 1; i < words.size(); i++) {
        if (counter != count_lowercase(words[i])) {
            return false;
        }
    }
    return true;
}

lub tak, ale to będzie trudniejsze do zrozumienia:

bool are_anagrams(const vector<string> &words)
{
    const string &head = words.front();

    return all_of(
        words.cbegin() + 1, words.cend(),
        [head](const string &word) {
            return is_permutation(word.cbegin(), word.cend(), 
                                  head.cbegin(), head.cend());
        }
    );
}
2

Proponujecie coraz dłuższe i coraz bardziej zwariowane kody, moja pierwsza wersja w tym temacie rozwiązuje problem dla dowolnej ilości słów w wierszu, a i tak jest krótsza od tego co podajecie.
Dla 5 słów w wierszu:

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;

int main()
{   
    ifstream fin("anagram.txt");
    ofstream afout("odp_4a.txt"),bfout("odp_4b.txt");
    for(string first,second;fin>>first;)
    {
        sort(begin(first),end(first));
        bool a=true,b=true;
        stringstream sin();
        sin<<first;
        for(int i=0;(i<4)&&(fin>>second);++i)
        {
            sin<<' '<<second;
            if(first.size()!=second.size()) a=b=false;
            else
            {
                sort(begin(second),end(second));
                if(first!=second) b=false;
            }
        }
        if(a) afout<<sin.str()<<endl;
        if(b) bfout<<sin.str()<<endl;
    }
    return 0;
}

EDIT:
Specjalnie dla @nalik aby mógł przeczytać:

#   include <iostream>
#   include <fstream>
#   include <sstring>
#   include <string>
#   include <algorithm>
using   namespace   std   ;

int   main   (   )
{   
    ifstream   fin   (   "anagram.txt"   )   ;
    ofstream   afout   (   "odp_4a.txt"   )   ,   bfout   (   "odp_4b.txt"   )   ;
    for   (   string   first   ,   second   ;   fin   >>   first   ;   )
    {
        sort   (   begin(   first   )   ,   end   (   first   )   )   ;
        bool   a   =   true   ,   b   =   true   ;
        stringstream   sin   (   )   ;
        sin   <<   first   ;
        for   (   int   i   =   1   ;   (   i   <   4   )   &&   (   fin   >>   second   )   &&   (   a   )   &&   (   b   )   ;   ++   i   )
        {
        	sin   <<   ' '   <<   second   ;
            if   (   first   .   size   (   )   !=   second   .   size   (   )   )   a   =   b   =   false   ;
            else
            {
                sort   (   begin   (   second   )   ,   end   (   second   )   )   ;
                if   (   first   !=   second   )   b   =   false   ;
            }
        }
        if   (   a   )   afout   <<   sin   .   str   (   )   <<   endl   ;
        if   (   b   )   bfout   <<   sin   .   str   (   )   <<   endl   ;
    }
    return   0   ;
}
2
#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <algorithm>

std::array<size_t, 128> wordHistogram(const std::string& s)
{
    std::array<size_t, 128> h{};
    for(auto ch : s) ++h[ch];
    return h;
}

using Row = std::array<std::string, 5>;

std::istream& load(std::istream& in, Row& data)
{
    for(auto& s : data) in >> s;
    return in;
}

std::ostream& store(std::ostream& out, const Row& data)
{
    for(auto& s : data) out << s << ' ';
    return out << '\n';
}

bool all_same_len(const Row& row)
{
    auto len = row.front().size();
    return std::all_of(row.begin() + 1, row.end(), [len](const auto& s) { return len == s.size();});
}

bool all_anagrams(const Row& row)
{
    auto hist= wordHistogram(row.front());
    return std::all_of(row.begin() + 1, row.end(), [hist](const auto& s) { return hist == wordHistogram(s);});
}

int main()
{
    std::ifstream in{"anagram.txt"};
    std::ofstream same_length{"odp_4a.txt"};
    std::ofstream anagrams{"odp_4b.txt"};
    Row row;
    while (load(in, row)) {
        if (all_same_len(row)) {
            store(same_length, row);
            if (all_anagrams(row)) store(anagrams, row);
        }
    }

    return 0;
}

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