Odszyfrowywanie trzy-literowych słów

0

Witam szanowną społeczność,
Dziś próbowałem rozwiązać kilka zadań z programowania. Niestety wyniki nie pokrywają się z 'poprawnymi' odpowiedziami. Poprawnymi dałem w apostrofy, ponieważ nie do końca zgadzam się z werdyktem sędziego.

Byłbym wdzięczny za zerknięcie w kod, gdyż problem jest trywialny dla osób, które obcują już trochę z algorytmiką. Dodałem także komentarze, aby kod był przejrzysty dla czytelników zewnętrznych, czyli dla Was. Zapraszam do zapoznania się z treścią a następnie z kodem. Dziękuję z góry za komentarze, odpowiedzi i rady.

Zadanie: link

Mój kod:

#include <bits/stdc++.h>

using namespace std;


char arr[5][5];
int row[200]; //tablica przechowująca rząd litery, np. wartość row['A'], czyli row[65] będzie równa wierszowi w tablicy dwuwymiarowej arr
int col[200]; //analogicznie tylko dla komulmn

bool let(char c) {
	return (c >= 'A' and c <= 'Z'); //zwraca true, gdy znak jest dużą literą
}

int main() {
	string alp; cin >> alp; //wczytanie alfabetu
	int k = 0; //zmienna pomocnicza
	for (int i = 0; i < 5; i ++) {
		for (int j = 0; j < 5; j ++) {
			//wczytywanie kolejnych liter apl[k] do tablicy arr[i][j] oraz zapisanie numerów wiersza i kolumny
			arr[i][j] = alp[k ++];
			col[arr[i][j]] = j;
			row[arr[i][j]] = i;
		}
	}

	string str;
	getline(cin, str); //pozbycie sie bufora '\n' z cin'a
	getline(cin, str); //wczytanie zaszyfrowanej wiadomości
	char x, y; // zmienne pomocnicze (str[i] zajmuje wiecej miejsca niz x albo y jesli chodzi o zapis i kod jest czytelniejszy)
	for (int i = 0; i < str.size() - 1;) {
		x = str[i]; y = str[i + 1];
		//kolejne warunki (tutaj nie ma co wyjasniac, wystarczy przeczytac tresc zadania :D)
		if (let(x) and let(y)) {
			if (row[x] == row[y]) {
				str[i] = arr[row[x]][(col[x] + 4) % 5];
				str[i + 1] = arr[row[y]][(col[y] + 4) % 5];
			}
			else if (col[x] == col[y]) {
				str[i] = arr[(row[x] + 4) % 5][col[x]];
				str[i + 1] = arr[(row[y] + 4) % 5][col[y]];
			}
			else {
				str[i] = arr[row[x]][col[y]];
				str[i + 1] = arr[row[y]][col[x]];
			}
			//nie zwiekszam i za kazdym razem, wiec musze przesunac o 2 za kazdym razem, gdy odszyfruje jakies 2 litery
			i += 2;
		}
		//jezeli napotkam na spacje, inkrementuje i
		else i ++;
	}
	cout << str;
}

Testy, które nie przeszły:
7 wiersz 1: wczytano 'JTG', a oczekiwano 'JTU'
8 wiersz 1: wczytano 'JTG', a oczekiwano 'JTU'
9 wiersz 1: wczytano 'A', a oczekiwano 'P'
10 wiersz 1: wczytano 'AAFWCFLAB', a oczekiwano 'AAFWCFLAZ'

Opcjonalnie w postaci wizualnej:
screenshot-20221209222550.png

0

(col[x] + 4) % 5 - "... zastępujemy je literami z prawej strony ..."

0

ze padających test case'ów wynika, że źle obsługujesz przypadek, gdy liczba liter szyfrogramu jest nieparzysta.
A powinien być to najprostszy przypadek:

Jeśli występowała nieparzysta liczba liter, ostatniej nie zamieniał.

Tu przerobiłem twój kod by oddzielić wczytywanie danych od kodu rozwiązującego zadanie. Przy okazji pozmieniałem irytujące mnie rzeczy: https://godbolt.org/z/q78q8xsrz

Po takiej zmianie, można twój kod poddać zautomatyzowanym testom: https://godbolt.org/z/qh1Eq7Pv1
Na pierwszy rzut oka wygląda, że twój kod jest poprawny (nie analizowałem go dokładnie). Czy jesteś w stanie dokopać się do danych wejściowych dla których sędzia odrzuca twój kod?

Z moich testów wynika, że źle uwzględniasz spacje: https://godbolt.org/z/hzoPEj3xb

0

https://ideone.com/tlyDDP
Juz dziala; nie przeczytalem ze zrozumieniem tresci. Najpierw trzeba bylo pozbyc sie spacji, potem odszyfrowac a na koniec przywrocic spacje.

0

Da się bez zmiennych globalnych, bez pozbywania się spacji oraz bez wyważania otwartych drzwi (mam na myśli let()):

#include <iostream>
#include <iomanip>
#include <vector>
#include <cctype>
#include <cstdlib>
using namespace std;

int main()
{
	struct coord { int y,x; };
	vector<coord> map(26,coord{-1,-1});
	vector<vector<char>> tb(5,vector<char>(5));
	for(int y=0;y<5;++y)
	{
		for(int x=0;x<5;++x)
		{
			char ch;
			cin>>ch;
			map[ch-'A']=coord{y,x};
			tb[y][x]=ch;
		}
	}
	while(cin.get()!='\n') {}
	while(cin)
	{
		char a,b;
		int spa=0,spb=0;
		while(isspace(a=cin.get())) ++spa;
		while(isspace(b=cin.get())) ++spb;
		if(isspace(a)) cout<<setw(spa)<<"";
		else if(isspace(b)) cout<<setw(spa)<<""<<a<<setw(spb)<<"";
		else
		{
			coord ca=map[a-'A'],cb=map[b-'A'];
			if(ca.y==cb.y)
			{
				a=tb[ca.y][(ca.x+4)%5];
				b=tb[cb.y][(cb.x+4)%5];
			}
			else if(ca.x==cb.x)
			{
				a=tb[(ca.y+4)%5][ca.x];
				b=tb[(cb.y+4)%5][cb.x];
			}
			else
			{
				a=tb[ca.y][cb.x];
				b=tb[cb.y][ca.x];
			}
			cout<<setw(spa)<<""<<a<<setw(spb)<<""<<b;
		}
	}	
	return 0;
}
0
MarekR22 napisał(a):

Z moich testów wynika, że źle uwzględniasz spacje: https://godbolt.org/z/hzoPEj3xb

Odpowiedź na samego siebie, bardziej, by było widać kod na forum a nie pod linkiem. Wygląda na to, że przez wielokrotne edycje mogło być przegapione to i owo.
Kod jest dłuższy, bo widać w nim:

  • mój kod (moją wersję rozwiązania z dbałością o czytelność)
  • refactor kodu od @polandonion (OP) - by dało się przetestować
  • testy w Catch2 uruchamiające obie wersje rozwiązania
  • i to, że kod OP, nie przechodzi przez testy, gdzie są spacje szczególnie jak pojawiają się nieparzyste sekcje.
#include "catch2/catch_all.hpp"

#include <string>
namespace my {
class Decoder {
    static constexpr size_t Size = 5;
    struct LetterPos {
        int row;
        int col;
    };
    std::array<LetterPos, 'Z' - 'A' + 1> letterPos = {};
    std::array<std::array<char, Size>, Size> mat = {};

    void initializeLookups(const std::string& key)
    {
        assert(key.size() == Size * Size);
        auto it = key.begin();
        LetterPos pos = {};
        for (auto& row : mat) {
            pos.col = 0;
            for (auto& ch : row) {
                ch = *it;
                assert(toEncode(ch));
                assert(ch != 'X');
                letterPos[letterToIndex(ch)] = pos;
                ++pos.col;
                ++it;
            }
            ++pos.row;
        }
    }

    static bool toEncode(char c)
    {
        return (c >= 'A' && c <= 'Z' && c != 'X');
    }

    static size_t letterToIndex(char ch)
    {
        return static_cast<unsigned>(ch - 'A');
    }

    LetterPos posFor(char ch) const
    {
        return letterPos[letterToIndex(ch)];
    }

    template <typename It>
    static It findLetter(It i, It end)
    {
        return std::find_if(i, end, toEncode);
    }

    template <typename It>
    static It next(It i, It end)
    {
        if (i != end) {
            ++i;
        }
        return findLetter(i, end);
    }

    std::pair<char, char> decode(LetterPos a, LetterPos b) const
    {
        if (a.row == b.row) {
            return {
                mat[a.row][(a.col + Size - 1) % Size],
                mat[b.row][(b.col + Size - 1) % Size]
            };
        }
        if (a.col == b.col) {
            return {
                mat[(a.row + Size - 1) % Size][a.col],
                mat[(b.row + Size - 1) % Size][b.col]
            };
        }
        return { mat[a.row][b.col], mat[b.row][a.col] };
    }

public:
    std::pair<char, char> decode(char a, char b) const
    {
        return decode(posFor(a), posFor(b));
    }

    std::string decode(std::string str) const
    {
        auto a = findLetter(str.begin(), str.end());
        auto b = next(a, str.end());
        while (a != str.end() && b != str.end()) {
            std::tie(*a, *b) = decode(*a, *b);
            a = next(b, str.end());
            b = next(a, str.end());
        }
        return str;
    }

    explicit Decoder(const std::string& key)
    {
        initializeLookups(key);
    }
};
}

namespace yours {

class Decoder {
    char arr[5][5] = {};
    int row[200] = {}; // tablica przechowująca rząd litery, np. wartośćrow['A'], czyli row[65] będzie równa wierszowi w tablicy dwuwymiarowej arr
    int col[200] = {}; // analogicznie jak powyżej

    static bool isupper(char c)
    {
        return (c >= 'A' && c <= 'Z');
    }

public:
    std::string decode(std::string str)
    {
        char x, y; // zmienne pomocnicze (str[i] zajmuje wiecej miejsca niz x albo y jesli chodzi o zapis i kod jest czytelniejszy)
        for (int i = 0; i < str.size() - 1;) {
            x = str[i];
            y = str[i + 1];
            // kolejne warunki (tutaj nie maco wyjasniac, wystarczy przeczytac tresc zadania :D)
            if (isupper(x) && isupper(y)) {
                if (row[x] == row[y]) {
                    str[i] = arr[row[x]][(col[x] + 4) % 5];
                    str[i + 1] = arr[row[y]][(col[y] + 4) % 5];
                } else if (col[x] == col[y]) {
                    str[i] = arr[(row[x] + 4) % 5][col[x]];
                    str[i + 1] = arr[(row[y] + 4) % 5][col[y]];
                } else {
                    str[i] = arr[row[x]][col[y]];
                    str[i + 1] = arr[row[y]][col[x]];
                }
                // nie zwiekszam i za kazdym razem, wiec musze przesunac o 2 za kazdym razem, gdy odszyfruje jakies 2 litery
                i += 2;
            }
            // jezeli napotkam na spacje, inkrementuje i
            else
                i++;
        }
        return str;
    }

    explicit Decoder(std::string key)
    {
        int k = 0; // zmienna pomocnicza
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                // wczytywanie kolejnych liter apl[k] do tablicy arr[i][j] oraz zapisanie numerów wiersza i kolumny
                arr[i][j] = key[k++];
                col[arr[i][j]] = j;
                row[arr[i][j]] = i;
            }
        }
    }
};
}

bool isValidTestString(const std::string& s)
{
    return std::all_of(s.begin(), s.end(), [](char ch) {
        return (std::isalpha(ch) == 0 || std::isupper(ch) != 0) && ch != 'X';
    });
}

TEST_CASE("Foo")
{
    SECTION("alphabet as key")
    {
        const std::string key { "ABCDEFGHIJKLMNOPQRSTUVWYZ" };
        yours::Decoder decoder { key };
        my::Decoder myDecoder { key };
        auto [encrypted, decrypted] = GENERATE(table<std::string, std::string>({
            //{ "", "" },
            { "A", "A" },
            { "Z", "Z" },
            { "P", "P" },
            { "QQ", "PP" },
            { "AA", "EE" },
            { "SS", "RR" },
            { "AB", "EA" },
            { "AF", "UA" },
            { "AZ", "EU" },
            { "AG", "BF" },
            { "WK", "UM" },

            { "QQQ", "PPQ" },
            { "AAD", "EED" },
            { "SSG", "RRG" },
            { "ABT", "EAT" },
            { "AFI", "UAI" },
            { "AZE", "EUE" },
            { "AGN", "BFN" },
            { "WKT", "UMT" },

            { "JTU", "EOU" },
            { "YG NGDAWK", "VI LICEUM" },
            { "DVD BQGK OJD DVC", "BYC ALBO NIE BYC" },
        }));

        REQUIRE(isValidTestString(encrypted));
        REQUIRE(isValidTestString(decrypted));
        CAPTURE(encrypted);
        REQUIRE(myDecoder.decode(encrypted) == decrypted);
        REQUIRE(decoder.decode(encrypted) == decrypted);
    }
}

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