Odwracanie kolejności znaków w stringu.

0

Witam! Muszę napisać program, który będzie sprawdzał czy podany wyraz jest palindromem (Dokładniej lista 200 wyrazów zapisanych w pliku tekstowym). Moim zdaniem niezbędnym do tego elementem jest odwrócenie części wyrazu i porównanie go z drugą połową. Jednak napisany przeze mnie kod nie działa zbyt dobrze. Zamiast odwrotności pojawiają się losowe znaki. Nie wiem o co chodzi i proszę o pomoc. PS. Jestem w tym zielony, C++ uczę się, gdyż C#, mojego dotychczasowego języka, nie można zdawać na maturze :/

Kod odpowiedzialny za odwracanie w przypadku wyrazu o parzystej liczbie znaków:

string b = wyraz.substr(dlugosc / 2, dlugosc);
                string odwrotnosc = "";
                for (int j = dlugosc; j > (dlugosc / 2); --j)
                {
                    odwrotnosc += b[j - 1];
                }
                
                cout << odwrotnosc << endl; // test - wyswietla pustke (?)

Oraz nieparzystej:

string b = wyraz.substr(dlugosc - 1 / 2, dlugosc);
                string odwrotnosc = "";
                for (int j = dlugosc; j > (dlugosc - 1 / 2); --j)
                {
                    odwrotnosc += b[j - 1];
                }
                
                cout << odwrotnosc << endl; // test - wyswietla losowe 2 znaki 

Cały program:

 #include <iostream>
#include <conio.h>
#include <string>
#include <fstream>

using namespace std;

int main()
{
    fstream plik;
    fstream wynik;
    plik.open("hasla.txt", std::ios::in | std::ios::out);
    wynik.open("wynik4b.txt", std::ios::out);
    
    string wyraz;
    for(int i = 0; i < 200; ++i)
    {
            plik >> wyraz;
            int dlugosc = (int)wyraz.length();
            if ((dlugosc % 2) == 0)
            {
                string b = wyraz.substr(dlugosc / 2, dlugosc);
                string odwrotnosc = "";
                for (int j = dlugosc; j > (dlugosc / 2); --j)
                {
                    odwrotnosc += b[j - 1];
                }
                
                cout << odwrotnosc << endl; // test - wyswietla pustke (?)
                if(wyraz.substr(0, dlugosc / 2) == odwrotnosc)
                {
                    wynik << wyraz << endl;
                }
            }
            else
            {
                string b = wyraz.substr(dlugosc - 1 / 2, dlugosc);
                string odwrotnosc = "";
                for (int j = dlugosc; j > (dlugosc - 1 / 2); --j)
                {
                    odwrotnosc += b[j - 1];
                }
                
                cout << odwrotnosc << endl; // test - wyswietla losowe 2 znaki
                if(wyraz.substr(0, (dlugosc - 1) / 2) == odwrotnosc)
                {
                    wynik << wyraz << endl;
                }
            }
                                     
    }
    
    getch();
    plik.close();
    wynik.close();
    return 0;
}
1

Ja zrobiłbym to tak:

bool is_palindrome(const std::string &str) {
  // iterator do poczatku
  std::string::const_iterator forward = str.begin();
  // iterator do konca
  std::string::const_reverse_iterator backward = str.rbegin();
 
  // dla stringa o dlugosci n wystarczy floor(n/2) porownan
  for (unsigned int i = 0; i < str.length() / 2; ++i) {
   // jezeli znaki od przodu i od tylu sie nie zgadzaja nie ma sensu szukac dalej
    if (*forward != *backward) {
      return false;
    }
 
    ++forward;
    ++backward;
  }
 
  return true;
}

Możesz spróbować tego użyć. Mam nadzieję, że zachęci Cię to to przestudiowania możliwości biblioteki standardowej C++.

0

Dziękuję za bardzo dobrą odpowiedź. Niestety nie będę jej póki co stosować, gdyż tak jak napisalem c++ uczę się do matury, a takie rzeczy jak std::const_reverse_iterator raczej nie nauczę się tak szybko na pamięć, bo i szkoda na to czasu. Co do drugiej części kodu, można to rozwiązać w ten sposób i jest to pewnie nawet lepsze rozwiązanie, ale najlepiej uczyć się na błędach :) Więc nadal proszę o sprawdzenie co jest nie tak w kodzie wyżej.

1

Twój kod nie działa, bo b[j - 1] nie ma sensu. Wartość j wynosi na początku tyle, co długość wyrazu, ale z substr dostajesz tylko połowę. Poza tym substr jest użyte nieprawidłowo, bo drugim parametrem jest ilość znaków a nie końcowa pozycja. Dodatkowo nie musisz rzutować tego, co zwraca length.

Porównaj to z kodem z użyciem iteratorów - żadnej zbędnej arytmetyki, mniej miejsc na pomyłkę. Jeżeli jednak iteratory Cię przerażają możesz zrobić to tak: (to w zasadzie identyczny kod)

bool is_palindrome(const std::string &str) {
  const size_t len = str.length() - 1;
 
  for (unsigned int i = 0; i < str.length() / 2; ++i) {
    if (str[i] != str[len - i]) {
      return false;
    }
  }
 
  return true;
}
0

Sposob Endriu jest dobry i prosty, nie wiem co w tym trudnego(sam tak to zrobilem). Nie chcesz iteratorow to zrob sobie dwie zmienne
int x=0;
int y=strlen(wyraz)-1;

for(x=0; x<strlen(wyraz)/2; x++)
if(wyraz[x]!=wyraz[y])
return false;
else
y--;

Pozdro

0

A więc jak się okazało, wina leżała po stronie wyjścia poza zakres tablicy b[j-1]. I tego właśnie nienawidzę w c++, gdy w C# wychodzę poza wymiary, dostaję OutOfRangeException, a tutaj muszę analizować cały kod...

0

Winisz język za to, że go nie znasz? Zamiast [] mogłeś użyc at, które rzuca wyjątek.

0

Nie winię go za to, że go nie znam, lecz za to, że gdy ja zrobię coś źle, to debugger/kompilator powinien mnie o tym informować. A w C++ mogę to tylko zobaczyć po działaniu programu. Po prostu średniowieczny debuger :S (bez obrazy dla zagorzałych fanów C++)

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