Machineboy空

Excercism - CryptoSquare : 문자열 ✅ 본문

Computer/Coding Test

Excercism - CryptoSquare : 문자열 ✅

안녕도라 2025. 1. 22. 18:59

문제요약

암호문을 출력하라. 과정은 아래와 같다.

 

input If man was meant to stay on the ground, god would have given us roots.
normalize ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots
segment "ifmanwas"
"meanttos"
"tayonthe"
"groundgo"
"dwouldha"
"vegivenu"
"sroots "
encoded imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau
2차 segment "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
output
"imtgdvs"
"fearwer"
"mayoogo"
"anouuio"
"ntnnlvt"
"wttddes"
"aohghn "
"sseoau "

https://exercism.org/tracks/csharp/exercises/crypto-square/edit


난이도

Medium


풀이 포인트

 

풀이 포인트라기엔, 추후 공부해야 하는 몰랐던 문법 추려두겠다.

  • Char.IsLetterOrDigit
  • Math.Sqrt
  • Math.Ceiling
  • Math.Min
  • string.Join() 

REVIEW

 

아니 Excercism Medium 난이도 꽤 어렵다.

백준으로 silver 3 ~ 5는 될 것 같다...

워낙 암호화 단계가 길어서 찬찬히 살펴 보자.


1) normalize                                                                

  • space and punctuation are removed : Char.IsLetterOrDigit
  • down-cased : ToLower()
using System;
using System.Linq;

public static string NormalizedPlaintext(string plaintext)
{
	// Char.IsLetterOrDigit : a letter or a decimal digit 인지 검사 하여 true/false
	string normalized = new string(plaintext.Where(char.IsLetterOrDigit).ToArray());
    
    // ToLower() : 소문자로 변경
    normalized = normalized.ToLower();
}

 

2) 1차 segment                                                                

최대한 정사각형에 가깝게 정리한다.

  • r * c >= length of message
  • c >= r
  • c - r <= 1
using System;
using System.Collections.Generic;
using System.Linq;

public static class CryptoSquare
{
	private static int c = 0;
    private static List<string> seg = new List<string>();
    
    public static IEnumerable<string> PlaintextSegments(string plaintext)
    {
    	int length = plaintext.Length;
        
        double squareRoot = Math.Sqrt(length);		// Math.Sqrt : 제곱근 구하기
        c = (int)Math.Ceiling(squareRoot);			// Math.Ceiling: 크거나 같은 최소 정수값 반환
        
        for(int i = 0; i < length; i += c)
        {
        	// Substring(시작 인덱스, 길이)
        	string segment = plaintext.Substring(i, Math.Min(c, length - i));
            seg.Add(segment);
        }
        
        return seg;
    }
}

 

3) encoded                                                                

 

segment 결과를 세로로 읽는다.

// 내가 시도한 코드.. 몇개의 에러 케이스가 뜬다..

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

public static class CryptoSquare
{
    // Normalize the plaintext by removing non-alphanumeric characters and converting to lowercase
    public static string NormalizedPlaintext(string plaintext)
    {
        return new string(plaintext.Where(char.IsLetterOrDigit).ToArray()).ToLower();
    }

    // Break plaintext into segments of size c
    public static IEnumerable<string> PlaintextSegments(string plaintext)
    {
        int length = plaintext.Length;
        double squareRoot = Math.Sqrt(length);
        int c = (int)Math.Ceiling(squareRoot);

        var segments = new List<string>();

        for (int i = 0; i < length; i += c)
        {
            string segment = plaintext.Substring(i, Math.Min(c, length - i));
            segments.Add(segment);
        }

        return segments;
    }

    // Encode the plaintext by reading columns of the plaintext segments
    public static string Encoded(string plaintext)
    {
        var segments = PlaintextSegments(plaintext).ToList();

        if (!segments.Any()) return string.Empty;

        int c = segments.First().Length;
        var encodedSegments = new List<string>();

        for (int i = 0; i < c; i++)
        {
            string encoded = "";

            foreach (string segment in segments)
            {
                if (i < segment.Length)
                {
                    encoded += segment[i];
                }
            }

            encodedSegments.Add(encoded);
        }

        // Join with a trailing space included
        return string.Join(" ", encodedSegments) + " ";
    }

    // Return the final ciphertext
    public static string Ciphertext(string plaintext)
    {
        if (string.IsNullOrWhiteSpace(plaintext)) return string.Empty;

        string normalized = NormalizedPlaintext(plaintext);
        return Encoded(normalized);
    }
}

 


CODE

// 모범 풀이

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

public static class CryptoSquare
{
    public static string Ciphertext(string plaintext) =>
        plaintext.Length == 0 ? string.Empty : string.Join(" ", plaintext.Normalized().Rows().Cols());

    private static string Normalized(this string str) =>
        new(str.ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray());

    private static int Size(this string str) => (int)Math.Ceiling(Math.Sqrt(str.Length));

    private static string[] Rows(this string str)
    {
        var size = str.Size();
        return str.Chunks(size).Select(row => row.PadRight(size)).ToArray();
    }
    
    private static IEnumerable<string> Cols(this string[] rows) =>
        Enumerable.Range(0, rows[0].Length)
            .Select(i => new string(rows.Select(row => row[i]).ToArray()));

    private static IEnumerable<string> Chunks(this string str, int size)
    {
        for (var i = 0; i < str.Length; i += size)
            yield return str.Substring(i, Math.Min(str.Length - i, size));
    }
}
using System;
using System.Collections.Generic;

public static class CryptoSquare
{
    public static string NormalizedPlaintext(string plaintext)
    {
        string normalized="";
        foreach (char c in plaintext)
        {   
            char lc = Char.ToLower(c);
            if ((lc>='a' && lc <='z') || (lc<='9' && lc>='0')) {
                normalized += lc;
            }
        }
        return normalized;
    }
      
    public static IEnumerable<string> PlaintextSegments(string plaintext)
    {
       List<string> seg = new List<string>();
       string input = NormalizedPlaintext(plaintext);
       Console.WriteLine("normalized_plain_text="+ input);
       int nlength= input.Length;
       if (nlength==0) return seg;
       int ncol = (int) Math.Ceiling(Math.Sqrt((double) nlength));
       int nrow = (int) Math.Ceiling((double) nlength/ (double) ncol);
       
        for (int ifrom = 0; ifrom< nlength; ifrom += ncol) {
            int  ilen = (ifrom + ncol >= nlength) ? (nlength-ifrom) : ncol;
            seg.Add(input.Substring(ifrom, ilen));
        }
        return seg;   
    }

    public static string Encoded(string plaintext)
    {
        return Ciphertext(plaintext);
    }

    public static string Ciphertext(string plaintext)
    {          
        string cipher="";
        if (plaintext.Length ==0) return cipher;
        List<string> segments = (List<string>) PlaintextSegments(plaintext);
        List<string> cipheredsegments = new List<string>();
        for (int jj=0; jj<segments[0].Length; jj++) {
            string colstring="";
            for (int ii=0; ii<segments.Count; ii++) {
                if (jj< segments[ii].Length) {
                    cipher += segments[ii][jj];
                    colstring += segments[ii][jj];
                } else colstring += ' '; 
            }
            cipheredsegments.Add(colstring);
        }
        string nct = "";
        for (int ii=0; ii< cipheredsegments.Count; ii++) {
            if (ii != 0) nct += " ";
            nct += cipheredsegments[ii];
        }
        return nct;
    }
}

아직 자꾸 내 코드는 테스트 케이스 2개 정도를 통과하지 못한다...