9019번: DSLR

네 개의 명령어 D, S, L, R 을 이용하는 간단한 계산기가 있다. 이 계산기에는 레지스터가 하나 있는데, 이 레지스터에는 0 이상 10,000 미만의 십진수를 저장할 수 있다. 각 명령어는 이 레지스터에

www.acmicpc.net

 

 

 

 


 

 

 

  • 풀이

 

DSLR 방식을 사용하여 최소의 방법으로 A에서 B의 값을 만들어내면 되는 문제입니다.

 

  • D: D 는 n을 두 배로 바꾼다. 결과 값이 9999 보다 큰 경우에는 10000 으로 나눈 나머지를 취한다. 그 결과 값(2n mod 10000)을 레지스터에 저장한다.
  • S: S 는 n에서 1 을 뺀 결과 n-1을 레지스터에 저장한다. n이 0 이라면 9999 가 대신 레지스터에 저장된다.
  • L: L 은 n의 각 자릿수를 왼편으로 회전시켜 그 결과를 레지스터에 저장한다. 이 연산이 끝나면 레지스터에 저장된 네 자릿수는 왼편부터 d2, d3, d4, d1이 된다.
  • R: R 은 n의 각 자릿수를 오른편으로 회전시켜 그 결과를 레지스터에 저장한다. 이 연산이 끝나면 레지스터에 저장된 네 자릿수는 왼편부터 d4, d1, d2, d3이 된다.

 

일단 위의 설명대로 하나씩 메소드를 만들어 값을 변경시킬 수 있도록 했습니다.

 

이제 정확하게 A에서 B로가는 최단명령어를 조사해야합니다. dfs를 사용할 경우 딥하게 들어가 stackoverflow가 발생할 수 있기때문에 bfs방식을 사용했습니다.

 

방식 ) class를 만들어 랜덤으로 DSLR을 하며 변경된 값과 command를 추가시켜주었습니다.

예외 ) check 배열을 만들어 같은 값이 또 나온 경우 같은 작업을 반복하지 않도록 해주었습니다.

 

문제에서 원하는 답은 같은 명령어의 길이이면 모두 정답이 되기 때문에 반복문을 통해 가장 먼저 B가 되는 값을 return 시켜주었습니다.

 

 

  • 코드

 

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
	static StringBuilder sb;
	static boolean[] check;
	
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		SetData();
		System.out.println(sb);
	}
	
	private static void SetData() throws Exception {
		InputReader in = new InputReader(System.in);
		
		int testcase = in.nextInt();
		sb = new StringBuilder();
		
		for(int i = 0; i < testcase; i++) {
			int a = in.nextInt();
			int b = in.nextInt();
			check = new boolean[10000];
			
			sb.append(bfs(a,b)).append("\n");
		}
	}
	
	private static String bfs(int a, int b) {
		Queue<Node> queue = new LinkedList<>();
		check[a] = true;
		queue.add(new Node(a, ""));
		
		while(!queue.isEmpty()) {
			Node node = queue.poll();
			
			if(node.value == b) {
				return node.command;
			}
			
			int valueAfterCommand = D(node.value);
			if(!check[valueAfterCommand]) {
				check[valueAfterCommand] = true;
				queue.add(new Node(valueAfterCommand, node.command+"D")) ;
			}
			
			valueAfterCommand = S(node.value);
			if(!check[valueAfterCommand]) {
				check[valueAfterCommand] = true;
				queue.add(new Node(valueAfterCommand, node.command+"S")) ;
			}
			
			valueAfterCommand = L(node.value);
			if(!check[valueAfterCommand]) {
				check[valueAfterCommand] = true;
				queue.add(new Node(valueAfterCommand, node.command+"L")) ;
			}
			
			valueAfterCommand = R(node.value);
			if(!check[valueAfterCommand]) {
				check[valueAfterCommand] = true;
				queue.add(new Node(valueAfterCommand, node.command+"R")) ;
			}
		}
		
		return "";
	}
	
	private static int D(int a) {
		return (a*2)%10000;
	}
	
	private static int S(int a) {
		a -=1;
		if(a==-1)
			return 9999;
		else
			return a;
	}
	
	private static int L(int a) {
		int temp = a%1000;
		int temp1 = a/1000;
		return temp*10+temp1;
	}
	
	private static int R(int a) {
		int temp = a % 10;
		int temp1 = a / 10;
		return temp*1000+temp1;
	}
}

class Node {
	int value;
	String command;
	
	public Node(int value, String command) {
		this.value = value;
		this.command = command;
	}
}

class InputReader {
	private final InputStream stream;
	private final byte[] buf = new byte[8192];
	private int curChar, snumChars;

	public InputReader(InputStream st) {
		this.stream = st;
	}

	public int read() {
		if (snumChars == -1)
			throw new InputMismatchException();
		if (curChar >= snumChars) {
			curChar = 0;
			try {
				snumChars = stream.read(buf);
			} catch (IOException e) {
				throw new InputMismatchException();
			}
			if (snumChars <= 0)
				return -1;
		}
		return buf[curChar++];
	}

	public int nextInt() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		int res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public long nextLong() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		long res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public int[] nextIntArray(int n) {
		int a[] = new int[n];
		for (int i = 0; i < n; i++) {
			a[i] = nextInt();
		}
		return a;
	}

	public String nextLine() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		StringBuilder res = new StringBuilder();
		do {
			res.appendCodePoint(c);
			c = read();
		} while (!isEndOfLine(c));
		return res.toString();
	}

	public boolean isSpaceChar(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	private boolean isEndOfLine(int c) {
		return c == '\n' || c == '\r' || c == -1;
	}
}

HTTP 프로토콜의 특징

  • 비연결 지향(connectionless) : 클라이언트가 request를 서버에 보내고, 서버가 클라이언트 요청에 맞는 response를 보내면 바로 연결을 끊음
  • 상태정보 유지안함(stateless) : 연결을 끊는 순간 클라이언트와 서버의 통신은 끝나며 상태 정보를 유지하지 않음

 

쿠키와 세션의 필요성

  • HTTP 프로토콜은 위와 같은 특성으로 모든 요청 간 의존관계가 없음
  • 즉, 현재 연결된 클라이언트가 이전의 연결된 클라이언트인지 아닌지 알 수 있는 방법이 없다.
  • 계속해서 연결을 유지하지 않기 때문에 리소스 낭비가 줄어드는 것이 큰 장점이지만, 통신할 때마다 새로 연결하기 때문에 클라이언트는 매 요청마다 인증을 해야한다는 단점이 있음.
  • 이전 요청과 현재 요청이 같은 사용자의 요청인지 알기 위해서는 상태를 유지해야함
  • HTTP 프로토콜에서 상태를 유지하기 위한 기술로 쿠키와 세션이 있다.

 

쿠키란?

  • 클라이언트 로컬에 저장되는 키와 값이 들어있는 파일이다.
  • 이름, 값, 유효 시간, 경로 등을 포함
  • 클라이언트의 상태정보를 브라우저에 저장하여 참조함
  • 구성 요소
    • 쿠키의 이름(Name)
    • 쿠키의 값(Value)
    • 쿠키의 만료시간(Expires)
    • 쿠키를 전송할 도메인 이름(Domain)
    • 쿠키를 전송할 경로(Path)
    • 보안 연결 여부(Secure)
    • HttpOnly 여부(HttpOnly)
  • 동작 방식
    1. 웹 브라우저가 서버에 요청
    2. 상태를 유지하고 싶은 값을 쿠키로 생성
    3. 서버가 응답할 때 HTTP 헤더(Set-Cookie)에 쿠키를 포함해서 전송
      ```java
      Set-Cookie : id=doy
      ```
    4. 전달받은 쿠키는 웹 브라우저에서 관리하고 있다가 다음 요청 때 쿠키를 HTTP 헤더에 넣어서 전송
      ```java
      Cookie : id=doy
      ```
    5. 서버에서는 쿠키 정보를 읽어 이전 정보를 확인 후 응답
  • 쿠키 사용 예 : 아이디, 비밀번호 저장, 쇼핑몰 장바구니

 

세션이란?

    • 일정 시간 동안 같은 브라우저로부터 들어오는 요청을 하나의 상태로 보고 그 상태를 유지하는 기술
    • 즉, 웹 브라우저를 통해 서버에 접속한 이후부터 브라우저를 종료할 때까지 유지되는 상태
    • 동작 방식
      1. 웹 브라우저가 서버에 요청
      2. 서버가 해당 브라우저(클라이언트)에 유일한 ID(세션 ID)를 부여
      3. 서버가 응답할 때 HTTP 헤더(Set-Cookie)에 세션 ID를 포함해서 전송, 쿠키에 세션 ID를 JSESSIONID 라는 이름으로 저장
        ```
        Set−Cookie: JSESSIONID=xslei13f
        ```

      4. 웹브라우저는 이후 웹브라우저를 닫기까지 다음 요청 때 부여된 세션 ID가 담겨있는 쿠키를 HTTP헤더에 넣어서 전송
        ```
        Cookie: JSESSIONID=xslei13f
        ```
      5. 서버는 세션 ID를 확인하고 해당 세션에 관련된 정보를 확인 후 응답
    • 세션 사용 예 : 로그인
    • 세션도 쿠키를 사용하여 값을 주고 받으며 클라이언트의 상태 정보를 유지한다. 즉, 상태 정보를 유지하는 수단은 쿠키

 

 

쿠키와 세션의 차이점

  • 저장 위치
    • 쿠키 : 클라이언트
    • 세션 : 서버
  • 보안
    • 쿠키 : 클라이언트에 저장됨으로 보안에 취약함
    • 세션 : 쿠키를 이용해 Session ID만 저장하고 이 값으로 구분해서 서버에서 처리하므로 비교적 보안성이 좋음
  • 라이프사이클
    • 쿠키 : 만료시간에 따라 브라우저를 종료해도 계속해서 남아 있을 수 있음
    • 세션 : 만료시간을 정할 수 있지만 브라우저를 종료하면 만료시간에 상관없이 삭제됨
  • 속도
    • 쿠키 : 클라이언트에 저장되어 있어 서버에 요청시 빠름
    • 세션 : 실제 저장된 정보가 서버에 있으므로 서버의 처리가 필요해 쿠키보다 느림

 

 

'Network' 카테고리의 다른 글

[Network] DNS 동작원리  (0) 2021.10.25
[Network] REST와 RESTful  (0) 2021.10.22
[Network] HTTP Method  (0) 2021.10.21
[Network] HTTP와 HTTPS  (0) 2021.10.21
[Network] 3-way-handshake와 4-way-hanshake  (0) 2021.10.17

HTTP Method 종류

  • GET
  • POST
  • DELETE
  • PUT

 

GET 메소드와 POST 메소드

  • HTTP 프로토콜을 이용해서 서버에 데이터(요청 정보)를 전달할 때 사용하는 방식

 

GET 메소드 방식

  • 개념
    • 정보를 조회하기 위한 메소드
    • 서버에서 어떤 데이터를 가져와 보여주기 위한 메소드
    • 가져오는 것(Select)
  • 사용 방법
    • URL 끝에 '?'가 붙고, 요청 정보가 (key=value)형태의 쌍을 이루어 ?뒤에 붙여서 서버로 전송
    • 요청 정보가 여러개 일 경우 '&'로 구분
    • ex) 127.23.123.323?name1=value1&name2=value2
  • 특징
    • URL에 요청 정보를 붙여서 전송
    • URL에 요청 정보가 뒤에 붙기때문에 요청 길이에 대해 제한이 있어서 대용량의 데이터를 전송하기 어려움
      • 한 번 요청 시 전송 데이터(주소값 + 파라미터)의 양은 255자로 제한됩니다.(HTTP/1.1은 2048자) 
    • 요청 정보를 사용자가 쉽게 확인할 수 있음
      • 그렇기때문에 보안에 취약합니다.
    • HTTP 패킷의 body는 비어있는 상태로 전송
      • 즉, Body의 데이터 타입을 표현하는 'Content-Type' 필드도 HTTP Request Header에 들어가지 않음
    • POST 방식보다 빠름
      • GET방식은 캐싱을 사용할 수 있다. GET 요청과 그에 대한 응답이 브라우저에 의해 캐싱

 

 

POST 메소드 방식

  • 개념
    • 서버의 값이나 상태를 바꾸기 위한 용도의 메소드
    • 수행하는 것 (Insert, Update, Delete)
  • 사용 방법
    • 요청 정보를 HTTP 패킷의 body안에 숨겨서 서버로 전송
    • Form 태그를 이용하여 submit하는 형태
    • Request Header의 Content-Type에 해당 데이터 타입이 표현되며, 전송하고자하는 데이터의 타입을 적어줘야함
      • Default : application/octet-stream
      • 단순 텍스트의 경우 : text/plain
      • 파일의 경우 : multipart/form-date
  • 특징
    • Body안에 요청 하는 데이터를 숨겨서 전송하기 때문에 보안상 GET방식보다 안전하고, 대용량의 데이터를 전송할 수 있음
    • 클라이언트 쪽에서 데이터를 인코딩하여 서버로 전송하고, 이를 받은 서버쪽이 데이터를 디코딩

 

 

PUT 메소드 방식

  • 개념
    • 리소스를 생성 / 업데이트하기 위해 서버로 데이터를 보내는 데 사용
  • 사용 방법
    • POST 방식과 비슷하지만 이미 있는 값의 UPDATE를 할 때 사용하여 Idempotent합니다.
  • 특징
    • 여러 번 요청해도 동일한 결과입니다. (Idempotent, 멱등)
    • Body의 수정할 값을 넣어서 서버로 전송합니다.

 

 

DELETE 메소드 방식

  • 정의
    •  지정된 리소스를 삭제합니다.
  • 사용방법
    • GET과 비슷한 방식으로 URL에 삭제할 데이터를 파라미터에 추가하여 서버에 전송합니다.
  • 특징
    • Body에 삭제할 데이터를 넣는 것이 아닌 URL에 포함시켜 전송합니다.

 

 

 

 

Q. 조회하기위한 용도로 사용할 때 POST가 아닌 GET 방식을 사용하는 이유?

  1.  설계원칙에 따라 GET방식은 서버에게 여러번을 요청해도 동일한 응답이 돌아와야함.(Idempotent, 멱등)
    • GET방식은 가져오는 것(Select)로, 서버의 데이터나 상태를 변화시키지 않아야함.
      • ex) 게시판의 리스트, 게시글 보기 기능
      • 예외) 방문자의 로그 남기기, 글을 읽은 횟수 증가 기능
    • POST방식은 수행하는 것으로, 서버의 값이나 상태를 바꾸기 위한 용도
      • ex) 게시판의 글 쓰기 기능
  2. 웹에서 모든 리소스는 Link할 수 있는 URL을 가지고 있어야함.
    • 어떤 웹페이지를 보고 있을 때 다른 사람한테 그 주소를 주기 위해서 주소창의 URL을 복사해서 줄 수 있어야 함.
    • 즉, 어떤 웹페이지를 조회할 때 원하는 페이지로 바로 이동하거나 이동시키기 위해서는 해당 링크의 정보가 필요
    • 이때 POST 방식을 사용할 경우에 값(링크의 정보)이 Body에 있기 때문에 URL만 전달할 수 있으므로 GET 방식을 사용해야 함. 그러나 글을 저장하는 경우 URL을 사용할 필요가 없기때문에 POST 방식을 사용함.

 

'Network' 카테고리의 다른 글

[Network] REST와 RESTful  (0) 2021.10.22
[Network] 쿠키와 세션  (0) 2021.10.21
[Network] HTTP와 HTTPS  (0) 2021.10.21
[Network] 3-way-handshake와 4-way-hanshake  (0) 2021.10.17
[Network] TCP와 UDP  (0) 2021.10.17

HTTP란?

HTTP는 www(world wide web)상에서 주고받을 수 있는 포로토콜입니다.

주로 html 문서를 주고받을 때 쓰입니다. TCP와 UDP를 사용하며, 80번 포트를 사용합니다.

 

HTTP1과 HTTP2의 차이

  • HTTP1과 HTTP2의 가장 큰 차이점은 속도가 향상 되었다는 점입니다.
  • 한번의 요청에 하나의 리소스만 받고 한 요청의 지연되면 뒤이은 요청들도 지연되는 특성을 가졌던 전 버전과 달리 스트림이라는 개념이 추가되어 한번에 다수의 요청과 응답을 처리하는 구조로 바뀐 차이점이 있습니다. 또한, 쿠키를 담아 큰 헤더를 가졌던 이전 버전과 달리 쿠키들을 허프만 코딩 압축을 통해 크기 자체를 압축시켰습니다.
  • HTTP1은 하나의 요청에 하나의 리소스만 처리하기 때문에 동시전송 문제와 다수의 리소스를 처리하기에 속도와 성능 이슈가 있었습니다.
    • 서버와 클라이언트가 1번 요청에 1개의 리소스만 받아 처리하기 때문에 처리속도가 느리다.
    • 쿠키가 헤더에 포함되어 크기가 큰 것도 속도가 느린 이유이다.
  • HTTP2가 HTTP1보다 속도가 빠른 이유
    • Multiplexed streams : 1번에 다수의 요청과 응답을 처리할 수 있습니다.
    • Server Push : 클라이언트가 서버에 요청하지 않아도 서버측에서 알아서 리소스를 보내줍니다.
    • Header compression : 허프만 코딩으로 쿠키의 크기를 압축하여 줄였습니다.

 

 

HTTP Method

  • GET : 입력 값으로 데이터를 바꾸지않고 리소스를 보여주기만 하는 용도일 때 사용하며 주소창에 입력한 값들이 포함되어 나옵니다.
  • POST : 입력 값으로 데이터를 저장, 업데이트 작업을 할 때 사용하며 주소창에 입력한 값들이 포함되어 나오지 않습니다. 
  • PUT : POST와 같은 역할을 하지만, POST와 달리 요청할 때 식별자를 보내야하며 몇 번을 수행해도 같은 값을 보장받고 싶을 때 사용합니다.
  • DELETE : 데이터를 삭제할 때 사용합니다.
  • HEAD
  • OPTIONS
  • TRACE
  • CONNECT

 

 

HTTP와 HTTPS

HTTP와 HTTPS는 HTML 같은 문서를 웹 브라우저가 웹 서버에 요청하는 프로토콜이라는 점이 같지만 통신 내용을 암호화하는 것이 다릅니다.

 

HTTPS의 S는 Secure socket, 즉 안전한 통신망이라는 뜻으로 페이지 암호화 키를 공개키로 암호화하고 인증하는 것입니다. 여기서 공개키란 데이터를 암호화하는데 키가 2개 필요하는 것입니다.

 

 

HTTP (HyperText Transfer Protocol)

  • 웹 상에서 클라이언트와 서버가 요청/응답(request/response)으로 정보를 주고 받을 수 있는 프로토콜(웹 통신 프로토콜)
  • 주로 html 문서를 주고받을 때 사용합니다.
  • TCP와 UDP를 사용하며, 80번 포트를 사용합니다.
  • 아무나봐도 상관없는 페이지에 HTTP를 사용합니다. (보안을 붙이지 않았기때문에 HTTPS보다 빠릅니다.)
  • 비연결(connectionless) : 클라이언트가 서버에 요청을 보내고 서버에서 적절한 응답을 클라이언트에게 보내면 연결은 바로 끊어집니다.
  • 무상태(stateless) : 연결을 끊는 순간 클라이언트와 서버의 통신은 끝나며 상태 정보를 유지하지 않습니다.

HTTPS (HyperText Transfer Protocol over secure Socket layer)

  • HTTP over TLS, HTTP Secure, HTTP over SSL
  • HTTP의 보완이 강화된 버전의 프로토콜
  • HTTPS의 기본 포트는 TCP/IP로 443번 포트를 사용합니다.
  • HTTPS는 소켓 통신에 일반 텍스트를 이용하는 대신에, 웹 상에서 정보S를 암호화하는 SSL이나 TLS 프로토콜을 통해 세션 데이터를 암호화합니다.
  • TLS(Transport Layer Security) 포로토콜은 SSL(Secure Socket Layer) 프로토콜에서 발전한 것 입니다.
  • 두 프로토콜의 주 목적은 기밀성(사생활 보호), 데이터 무결성, ID 및 디지털 인증서를 사용한 인증을 제공하는 것 입니다.
  • 보호의 수준은 웹 브라우저에서의 구현 정확도와 서버 소프트웨어, 지원하는 암호화 알고리즘에 달려있습니다.
  • 금융 정보나 메일 등의 중요한 정보를 주고 받는 것에 HTTPS를 사용합니다.

 

HTTPS가 필요한 이유

  • 클라이언트인 웹 브라우저가 서버에 HTTP를 통해 웹 페이지나 이미지 정보를 요청하면 서버는 이 요청에 응답하여 요구하는 정보를 제공합니다.
  • HTTP를 통해 웹 페이지(HTML or 텍스트 정보)를 교환합니다.
  • 이때, 주고받는 데이터에 주민번호나 비밀번호와 같은 중요한 정보가 포함된 상태로 네트워크상에서 제 3자가 정보를 가로챈다면 보안상 큰 위험이 있을 수 있습니다. 그래서 중간에서 정보를 볼 수 없도록 주고받는 정보를 암호화하는 방법인 HTTPS를 사용합니다.
  • HTTP를 사용할 경우 개인 정보가 아니더라도 사용자가 어느 사이트에 접근하는 지, 어떤 행동을 하는지 모두 감시할 수 있습니다. 이로 인해 정부나 ISP 가 사용자의 패턴을 파악해서 인터넷 검열을 할 수 있다는 문제가 있습니다.

 

HTTPS의 원리

  • 공개키, 개인키 알고리즘 방식
  • 암호화, 복호화시킬 수 있는 서로 다른 키(공개키, 개인키)를 이용한 암호화 방법
    • 공개키 : 모두에게 공개, 공개키 저장소에 등록
    • 개인키(비공개키) : 개인에게만 공개. 클라이언트-서버 구조에서는 서버가 가지고 있는 비공개키
  • 클라이언트 -> 서버
    • 사용자의 데이터를 공개키로 암호화 
    • 서버로 전송 (데이터를 가져가도 개인키가 없기 때문에 복호화할 수 없음)
    • 서버에서 개인키로 복호화하여 요청 처리

 

HTTPS의 장점

  • 네트워크 상에서 열람, 수정이 불가하므로 안전합니다.

 

HTTPS의 단점

  • 암호화하는 과정이 웹 서버에 부하를 줄 수 있습니다.
  • HTTPS는 설치 및 인증서를 유지하는데 추가 비용이 발생합니다.
  • 암호화, 복호화를 하기때문에 HTTP보다 느립니다.
  • 인터넷 연결이 끊긴 경우 재인증 시간이 소요됩니다.
    • HTTP는 비연결형으로 웹 페이지를 보는 중 중간에 연결이 끊겼다가 다시 연결되어도 페이지를 계속 볼 수 있습니다.
    • 그러나 HTTPS의 경우 소켓(데이터를 주고받는 경로) 자체에서 인증하기 때문에 인터넷 연결이 끊기면 소켓도 끊어져서 다시 HTTPS 인증이 필요합니다.

 

대칭키 암호화, 공개키 암호화 방법

  • 대칭키 암호화는 암호화 키와 복호화 키가 동일한 암호화 기법입니다. 암호키 자체가 암호화 되지 않은 평문으로 노출되면 보안에 매우 최악하여 키 전달 및 관리가 어려운 단점이 있고, 공개키 암호화 방식보다 속도가 빠르다는 장점이 있습니다.
    • 대칭키
    • 암/복호화 키가 동일
    • 대표 암호알고리즘 : DES, 3DES, TDES, AES, SEED, ARIA 등
    • 대체적으로 bit 수가 적고 수행시간이 짧다.
    • 사용에 제한적입니다.
    • 사용 예 :  디스크 암호화
  • 공개키 암호화는 대칭키 암호화와 다르게 개인키와 공개키 두 가지 키를 사용합니다. 개인키로 암호화된 문서는 공개키로만 복호화할 수 있으며, 반대로 공개키로 암호화된 문서는 개인키로만 복호화할 수 있습니다. 암호화, 복호화의 작업이 복잡하기 때문에  대칭키 암호화에 비해 시간이 오래 걸린다는 단점이 있습니다.
    • 비대칭키(공개키, 개인키)
    • 암/복호화 키가 다름
    • 대표 암호 알고리즘 : RSA, ECC 등
    • 대체적으로 bit 수가 많고 수행시간이 길다.
    • 공개라는 단어와 어울리게 범용적으로 사용 가능합니다.
    • 사용 예 : 비밀번호 암호화
  • 요즘 암호화 통신에서는 대칭키 암호화와 공개키 암호화의 좋은 점을 채택하여, 공개키 암호화를 사용하여 대칭키 암호화의 공통키를 공유하고, 그 키를 기반으로 실제 통신을 암호화하는 하이브리드 구조를 사용합니다.

 

SSL과 TLS (https://nuritech.tistory.com/25 - 조금 더 딥하게 정리되어 있습니다.)

  • SSL과 TLS는 클라이언트와 서버간의 통신을 암호화하는데 사용되는 프로토콜입니다.
  • SSL은 인터넷을 통해 전달되는 정보의 보안을 위해 개발한 인터넷 통신 규약 프로토콜이며, TLS는 안정성을 높이는 목적으로 SSL 3.0을 기초로하여 고안되었습니다.
  • 과정 : SSL/TLS는 TCP 레벨의 연결이 완료된 후에 다음과 같은 방법으로 동작합니다. (SSL handshake) 
    • 클라이언트와 서버가 기본적인 hello 메세지로 정보 송수신
    • 서버는 서버가 사용하는 SSL/TLS 인증서 전달
    • 클라이언트는 암호화에 사용할 키(대칭키 or 공개키)를 서버에 전달
    • 클라이언트는 암호화에 사용할 암호 알고리즘과 해시 알고리즘을 서버에 전달
    • 서버도 알고리즘 목록을 교환하여 키를 서버와 클라이언트 모두 소유

 

 

'Network' 카테고리의 다른 글

[Network] 쿠키와 세션  (0) 2021.10.21
[Network] HTTP Method  (0) 2021.10.21
[Network] 3-way-handshake와 4-way-hanshake  (0) 2021.10.17
[Network] TCP와 UDP  (0) 2021.10.17
[Network] OSI 7계층 & TCP/IP 4계층  (0) 2021.10.14

 

13023번: ABCDE

문제의 조건에 맞는 A, B, C, D, E가 존재하면 1을 없으면 0을 출력한다.

www.acmicpc.net

 

 

 

 


 

 

 

 

  • 풀이

 

처음에 문제를 읽고 이해가 되지않아서 해맸습니다.

 

정리하자면, 모든 노드 중 ABCDE의 예시처럼 5개의 노드가 예시의 입력이 주어지는 가를 맞추는 문제입니다.

즉, 모든 노드를 확인하며 ABCDE처럼 5개의 노드까지가면 1출력, 모든 노드가 ABCDE처럼 5개의 노드를 갈 수 없다면 0출력해주면 됩니다.

 

저는 ArrayList 배열을 통해 노드들을 연결했고, boolean 배열을 통해 방문을 체크해주었습니다.

돌리는 방식은 dfs방식으로 depth가 5이면 1을 출력하도록 했습니다.

 

 

  • 코드

 

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
	static ArrayList[] array;
	static boolean[] check;
	static int N, M, answer;
	
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		SetData();
		System.out.println(answer);
	}
	
	private static void SetData() throws Exception {
		InputReader in = new InputReader(System.in);
		
		N = in.nextInt();
		M = in.nextInt();
		answer = 0;
		array = new ArrayList[N];
		check = new boolean[N];
		for(int i = 0 ; i < N; i++)
			array[i] = new ArrayList<Integer>();
		
		
		for(int i = 0 ; i < M; i++) {
			int a = in.nextInt();
			int b = in.nextInt();
			
			array[a].add(b);
			array[b].add(a);
		}
		
		for(int i = 0 ; i < N; i++) {
			if(answer == 1) break;
			
			check[i] = true;
			dfs(i, 1);
			check[i] = false;
		}
	}
	
	private static void dfs(int index, int depth) {
		if(depth == 5) { 
			answer = 1;
			return;
		}
		
		for(int i = 0; i < array[index].size(); i++) {
			int temp = (int) array[index].get(i);
			if(check[temp]) continue;
			
			check[temp] = true;
			dfs(temp, depth+1);
			check[temp] = false;
		}
	}
}

class InputReader {
	private final InputStream stream;
	private final byte[] buf = new byte[8192];
	private int curChar, snumChars;

	public InputReader(InputStream st) {
		this.stream = st;
	}

	public int read() {
		if (snumChars == -1)
			throw new InputMismatchException();
		if (curChar >= snumChars) {
			curChar = 0;
			try {
				snumChars = stream.read(buf);
			} catch (IOException e) {
				throw new InputMismatchException();
			}
			if (snumChars <= 0)
				return -1;
		}
		return buf[curChar++];
	}

	public int nextInt() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		int res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public long nextLong() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		long res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public int[] nextIntArray(int n) {
		int a[] = new int[n];
		for (int i = 0; i < n; i++) {
			a[i] = nextInt();
		}
		return a;
	}

	public String nextLine() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		StringBuilder res = new StringBuilder();
		do {
			res.appendCodePoint(c);
			c = read();
		} while (!isEndOfLine(c));
		return res.toString();
	}

	public boolean isSpaceChar(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	private boolean isEndOfLine(int c) {
		return c == '\n' || c == '\r' || c == -1;
	}
}

 

1261번: 알고스팟

첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은 벽을 의미

www.acmicpc.net

 

 

 

 

 

 


 

 

 

 

 

  • 풀이

 

일반적인 배열의 너비 우선 탐색에서 dist배열을 추가하여 더 적게 벽을 부순경우의 수만 queue에 추가하여 탐색해주었습니다.

 

Queue를 int[]인 배열로 만들어서 변수명[0]으로 꺼내오는 것 보단 Node라는 클래스를 만들어 좌표의 뜻을 품는 x, y의 좌표로 표현하였습니다. (변수의 의미 부여)

 

  • 코드

 

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
	static int N, M;
	static int[][] array;
	static int[][] dist;
	static int[] x = {0,0,-1,1};
	static int[] y = {-1,1,0,0};
	
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		SetData();
		System.out.println(dist[N-1][M-1]);
	}
	
	private static void SetData() throws Exception {
		InputReader in = new InputReader(System.in);
		
		M = in.nextInt();
		N = in.nextInt();
		array = new int[N][M];
		dist = new int[N][M];
		
		for(int i = 0; i < N; i++) {
			String s = in.nextLine();
			for(int j = 0; j < M; j++) {
				array[i][j] = s.charAt(j) - '0';
				dist[i][j] = 10000000;
			}
		}
		
		bfs();
	}
	
	private static void bfs() {
		Queue<Node> queue = new LinkedList<>();
		queue.add(new Node(0,0));
		dist[0][0] = 0;
		
		while(!queue.isEmpty()) {
			Node node = queue.poll();
			
			for(int i = 0; i < 4; i++) {
				int r = node.x + x[i];
				int c = node.y + y[i];
				
				if(r < 0 || c < 0 || r >= N || c >= M) continue;
				
				if (dist[r][c] > dist[node.x][node.y] + array[r][c]) {
					dist[r][c] = dist[node.x][node.y] + array[r][c];
					queue.add(new Node(r,c));
				}
			}
		}
	}
}

class Node {
	int x;
	int y;
	
	public Node(int x, int y) {
		this.x = x;
		this.y = y;
	}
}

class InputReader {
	private final InputStream stream;
	private final byte[] buf = new byte[8192];
	private int curChar, snumChars;

	public InputReader(InputStream st) {
		this.stream = st;
	}

	public int read() {
		if (snumChars == -1)
			throw new InputMismatchException();
		if (curChar >= snumChars) {
			curChar = 0;
			try {
				snumChars = stream.read(buf);
			} catch (IOException e) {
				throw new InputMismatchException();
			}
			if (snumChars <= 0)
				return -1;
		}
		return buf[curChar++];
	}

	public int nextInt() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		int res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public long nextLong() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		long res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public int[] nextIntArray(int n) {
		int a[] = new int[n];
		for (int i = 0; i < n; i++) {
			a[i] = nextInt();
		}
		return a;
	}

	public String nextLine() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		StringBuilder res = new StringBuilder();
		do {
			res.appendCodePoint(c);
			c = read();
		} while (!isEndOfLine(c));
		return res.toString();
	}

	public boolean isSpaceChar(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	private boolean isEndOfLine(int c) {
		return c == '\n' || c == '\r' || c == -1;
	}
}

 

2458번: 키 순서

1번부터 N번까지 번호가 붙여져 있는 학생들에 대하여 두 학생끼리 키를 비교한 결과의 일부가 주어져 있다. 단, N명의 학생들의 키는 모두 다르다고 가정한다. 예를 들어, 6명의 학생들에 대하여

www.acmicpc.net

 

 

 

 


 

 

 

  • 풀이

 

풀이 방식은 플로이드와샬을 사용했습니다.

 

자신보다 우선순위가 큰 값은 (i,j)를 1로 작은 값은 -1로 초기화 시켰습니다.

 

플로이드와샬 방식을 통해 배열을 돌며 자신의 큰 값보다 더 큰 값 또한 (i,j)를 1로 작은 값보다 더 작은 값은 -1로 초기화 시켜주었습니다.

 

 

  • 코드

 

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
	static int N, M;
	static int[][] array;
	static int answer;
	
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		SetData();
		System.out.println(answer);
	}
	
	private static void SetData() throws Exception {
		InputReader in = new InputReader(System.in);
		
		N = in.nextInt();
		M = in.nextInt();
		answer = 0;
		array = new int[N+1][N+1];
		
		for(int i = 0 ; i < M; i++) {
			int a = in.nextInt();
			int b = in.nextInt();
			array[a][b] = 1;
			array[b][a] = -1;
		}
		
		for(int k = 1; k <= N; k++) {
			for(int i = 1; i <= N; i++) {
				for(int j = 1; j <= N; j++) {
					if(array[i][k] + array[k][k] == 2)
						array[i][j] = 1;
					else if(array[i][k] + array[k][j] == -2)
						array[i][j] = -1;
				}
			}
		}
		
		
		for(int i = 1; i <= N; i++) {
			int count = 0;
			for(int j = 1; j <= N; j++) {
				if(array[i][j] != 0 || array[j][i] != 0)
					count++;
			}
			if(count == N-1) answer++;
		}
	}
}

class InputReader {
	private final InputStream stream;
	private final byte[] buf = new byte[8192];
	private int curChar, snumChars;

	public InputReader(InputStream st) {
		this.stream = st;
	}

	public int read() {
		if (snumChars == -1)
			throw new InputMismatchException();
		if (curChar >= snumChars) {
			curChar = 0;
			try {
				snumChars = stream.read(buf);
			} catch (IOException e) {
				throw new InputMismatchException();
			}
			if (snumChars <= 0)
				return -1;
		}
		return buf[curChar++];
	}

	public int nextInt() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		int res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public long nextLong() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		long res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public int[] nextIntArray(int n) {
		int a[] = new int[n];
		for (int i = 0; i < n; i++) {
			a[i] = nextInt();
		}
		return a;
	}

	public String nextLine() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		StringBuilder res = new StringBuilder();
		do {
			res.appendCodePoint(c);
			c = read();
		} while (!isEndOfLine(c));
		return res.toString();
	}

	public boolean isSpaceChar(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	private boolean isEndOfLine(int c) {
		return c == '\n' || c == '\r' || c == -1;
	}
}

 

11403번: 경로 찾기

가중치 없는 방향 그래프 G가 주어졌을 때, 모든 정점 (i, j)에 대해서, i에서 j로 가는 경로가 있는지 없는지 구하는 프로그램을 작성하시오.

www.acmicpc.net

 

 

 

 


 

 

 

 

  • 풀이

 

문제는 그래프 (i,j)에서 i에서 j로 가는 경로가 있는지 없는지 구해주는 문제입니다.

 

문제를 읽자마자 가장 먼저 생각난 알고리즘은 플로이드와샬이였습니다.

 

왜? 플로이드와샬이 바로 생각났는지에 대해 궁금해 하시는 분들이 있을 수 있을 것 같아 간단하게 설명을 드리자면 플로이드 와샬의 목적에 대해서 생각해볼 필요가 있습니다.

 

플로이드와샬의 목적은 모든 정점에서 모든 정점으로의 최단경로를 찾는 것 입니다. 플로이드의 기본 코드를 보면 k인덱스를 기준으로 i에서 j로 가는 가장 작은 가중치를 초기화 시켜줍니다. 

플로이드의 초기화 기본 코드는 array[i][j] = Math.min(array[i][j], array[i][k]+array[k][j])입니다. 현재 초기화된 값보다 i에서 k로 가는 경로와 k에서 j로 가는 경로의 값을 더한 값이 더 작다면 값을 초기화 시켜준다는 것 입니다.

 

이를 이어서 생각하면 array[i][k] + array[k][j]의 값이 2인 경우 i->k로 가는 경우도 있고 k->j로 가는 경우도 있다는 것을 의미합니다. 따라서 현재 array[i][j]값이 1이 아니여도 array[i][k]로 가는 경로가 있고 array[k][j]로 가는 경우가 있다면 array[i][j]를 갈 수 있다를 토대로 코드를 작성했습니다.

 

 

  • 코드

 

 

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
	static int N;
	static int[][] array;
	static StringBuilder sb;
	
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		SetData();
		System.out.println(sb);
	}
	
	private static void SetData() throws Exception {
		InputReader in = new InputReader(System.in);
		
		N = in.nextInt();
		sb = new StringBuilder();
		array = new int[N][N];
		
		for(int i = 0; i < N; i++) {
			for(int j = 0; j < N; j++) {
				array[i][j] = in.nextInt();
			}
		}
		
		for(int k = 0; k < N; k++) {
			for(int i = 0; i < N; i++) {
				for(int j = 0; j < N; j++) {
					// j까지 도달한다면
					if(array[i][k] + array[k][j] == 2) {
						array[i][j] = 1;
					}
				}
			}
		}
		
		for(int i = 0; i < N; i++) {
			for(int j = 0; j < N; j++) {
				sb.append(array[i][j] + " ");
			}
			sb.append("\n");
		}
	}
}

class InputReader {
	private final InputStream stream;
	private final byte[] buf = new byte[8192];
	private int curChar, snumChars;

	public InputReader(InputStream st) {
		this.stream = st;
	}

	public int read() {
		if (snumChars == -1)
			throw new InputMismatchException();
		if (curChar >= snumChars) {
			curChar = 0;
			try {
				snumChars = stream.read(buf);
			} catch (IOException e) {
				throw new InputMismatchException();
			}
			if (snumChars <= 0)
				return -1;
		}
		return buf[curChar++];
	}

	public int nextInt() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		int res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public long nextLong() {
		int c = read();
		while (isSpaceChar(c)) {
			c = read();
		}
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		long res = 0;
		do {
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public int[] nextIntArray(int n) {
		int a[] = new int[n];
		for (int i = 0; i < n; i++) {
			a[i] = nextInt();
		}
		return a;
	}

	public String nextLine() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		StringBuilder res = new StringBuilder();
		do {
			res.appendCodePoint(c);
			c = read();
		} while (!isEndOfLine(c));
		return res.toString();
	}

	public boolean isSpaceChar(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	private boolean isEndOfLine(int c) {
		return c == '\n' || c == '\r' || c == -1;
	}
}

+ Recent posts