0%

[BaekJoon-10824] 네 수

문제

네 자연수 A, B, C, D가 주어진다. 이때, A와 B를 붙인 수와 C와 D를 붙인 수의 합을 구하는 프로그램을 작성하시오.

두 수 A와 B를 합치는 것은 A의 뒤에 B를 붙이는 것을 의미한다. 즉, 20과 30을 붙이면 2030이 된다.

입력

첫째 줄에 네 자연수 A, B, C, D가 주어진다. (1 ≤ A, B, C, D ≤ 1,000,000)

출력

A와 B를 붙인 수와 C와 D를 붙인 수의 합을 출력한다.

예제 입력 1

10 20 30 40

예제 출력 1

4060

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {

public static void main(String[] args) throws Exception {
solve();
}
public static void solve() throws Exception {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();
String[] split = str.split(" ");
long num1 = Long.parseLong(split[0] + split[1]);
long num2 = Long.parseLong(split[2] + split[3]);
out.write(num1+num2+"\n");
out.flush();
out.close();;
in.close();
}
}

parseLong를 직접 만들어서..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {

public static void main(String[] args) throws Exception {
solve2();
}


public static void solve2() throws Exception {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();
String[] split = str.split(" ");
long num1 = parseLong(split[0] + split[1]);
long num2 = parseLong(split[2] + split[3]);
out.write(num1+num2+"\n");
out.flush();
out.close();;
in.close();
}
public static long parseLong(String str) {
char[] chs = str.toCharArray();
int length = chs.length;
long exp = 1;
long result = 0;
int i = length -1;
while (i != -1) {
result += (chs[i--] - '0') * exp;
exp *= 10;
}
return result;
}
}

문제 사이트 : https://www.acmicpc.net/problem/10824

ROT13

문제

ROT13은 카이사르 암호의 일종으로 영어 알파벳을 13글자씩 밀어서 만든다.

예를 들어, “Baekjoon Online Judge”를 ROT13으로 암호화하면 “Onrxwbba Bayvar Whqtr”가 된다. ROT13으로 암호화한 내용을 원래 내용으로 바꾸려면 암호화한 문자열을 다시 ROT13하면 된다. 앞에서 암호화한 문자열 “Onrxwbba Bayvar Whqtr”에 다시 ROT13을 적용하면 “Baekjoon Online Judge”가 된다.

ROT13은 알파벳 대문자와 소문자에만 적용할 수 있다. 알파벳이 아닌 글자는 원래 글자 그대로 남아 있어야 한다. 예를 들어, “One is 1”을 ROT13으로 암호화하면 “Bar vf 1”이 된다.

문자열이 주어졌을 때, “ROT13”으로 암호화한 다음 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 알파벳 대문자, 소문자, 공백, 숫자로만 이루어진 문자열 S가 주어진다. S의 길이는 100을 넘지 않는다.

출력

첫째 줄에 S를 ROT13으로 암호화한 내용을 출력한다.

예제 입력 1

1
Baekjoon Online Judge

예제 출력 1

1
Onrxwbba Bayvar Whqtr

예제 입력 2

1
One is 1

예제 출력 2

1
Bar vf 1

해결

시저암호의 알고리즘은 secret key가 주어지면 문자를 secret key만큼 shift 하는 것으로

$f(x) = (x + key) mod 26 $ 이다.

대문자 소문자 구분은 Ascii 코드를 이용하여 대소문자를 구별할 수 있으며 문제는 N 이후로는 13번 shift를 하면 A 로 넘어가버린다.

이 문제를 해결하기 위해 key가 13 이니까 N은 N - A = 13 이므로 $26 mod 26 = 0$ 이 되므로

$(fx) = (x + (idx + key) modf 26$ 라는 수식을 얻을 수 있다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {

public static void main(String[] args) throws Exception {
rot13();
}

public static void rot13() throws Exception {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();
for (char ch : str.toCharArray()) {
if (ch >= 'a' && ch <= 'z') {
out.write(rot('a', ch, 13));
} else if (ch >= 'A' && ch <= 'Z') {
out.write(rot('A', ch, 13));
} else {
out.write(ch);
}
}
out.flush();
out.close();
in.close();
}

private static char rot(char ascii, char ch, int key) {
return (char) (ascii + ((ch - ascii) + key) % 26);
}
}

문제 사이트 : https://www.acmicpc.net/problem/11655

[BaekJoon-10809] 알파벳 찾기

문제

알파벳 소문자로만 이루어진 단어 S가 주어진다. 각각의 알파벳에 대해서, 단어에 포함되어 있는 경우에는 처음 등장하는 위치를, 포함되어 있지 않은 경우에는 -1을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 단어 S가 주어진다. 단어의 길이는 100을 넘지 않으며, 알파벳 소문자로만 이루어져 있다.

출력

각각의 알파벳에 대해서, a가 처음 등장하는 위치, b가 처음 등장하는 위치, … z가 처음 등장하는 위치를 공백으로 구분해서 출력한다.

만약, 어떤 알파벳이 단어에 포함되어 있지 않다면 -1을 출력한다. 단어의 첫 번째 글자는 0번째 위치이고, 두 번째 글자는 1번째 위치이다.

예제 입력 1

1
baekjoon

예제 출력 1

1
1 0 -1 -1 2 -1 -1 -1 -1 4 3 -1 -1 7 5 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;

public class Main {

public static void main(String[] args) throws Exception {
alphabetFound();
}

public static void alphabetFound() throws Exception {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();
char[] chars = str.toCharArray();
int[] result = new int [26];
Arrays.fill(result, -1);

for (int i = 0; i < chars.length; i++) {
int idx = chars[i] - 'a';
if (result[idx] == -1)
result[idx] = i;
}

for(int i : result) {
out.write(i + " ");
}
out.flush();
out.close();
in.close();

}
}

문제 사이트 : https://www.acmicpc.net/problem/10809

알파벳 개수

문제

알파벳 소문자로만 이루어진 단어 S가 주어진다. 각 알파벳이 단어에 몇 개가 포함되어 있는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 단어 S가 주어진다. 단어의 길이는 100을 넘지 않으며, 알파벳 소문자로만 이루어져 있다.

출력

단어에 포함되어 있는 a의 개수, b의 개수, …, z의 개수를 공백으로 구분해서 출력한다.

예제 입력 1

1
baekjoon

예제 출력 1

1
1 1 0 0 1 0 0 0 0 1 1 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0

해결

아스키코드를 이용하여 쉽게 풀 수 있다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {


public static void main(String[] args) throws Exception {
countOfAlphabet();
}

public static void countOfAlphabet() throws Exception {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();
int[] result = new int [26];
for (char ch : str.toCharArray()) {
result[ch - 'a']++;
}

for(int i : result) {
out.write(i + " ");
}
out.flush();
out.close();
in.close();

}
}

문제 사이트 : https://www.acmicpc.net/problem/10808

템플릿 리터럴(Template Literals)

ES6는 템플릿 리터럴(Template Literals)은 일반 문자열과 비슷해보이지만 ' 또는 " 같은 통상적인 따옴표 문자 대신 백틱(Backtick) (`) 문자를 사용한다.

1
2
3
const template = `템플릿 리터럴은 '작은따옴표(single quotes)'과 "큰따옴표(double quotes)"를 혼용할 수 있다.`;

console.log(template);

방법1

일반적인 문자열에서 줄바꿈은 허용되지 않고 공백(white-spce)를 표현하기 위해 백슬래시(\)로 시작하는 이스케이프 시퀀스(Escape Sequence)를 사용해야한다. 템플릿 리터럴은 일반적인 문자열과 다르게 여러 줄에 걸쳐 문자열을 작성할 수 있으며 템플릿 리터럴 내의 모든 white-space는 있는 그대로 적용된다.

1
2
3
4
5
6
7
8
const template = `<ul>
<li><a href="#home">Home</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul>`;

console.log(template);

[결과]

1
2
3
4
5
6
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul>

방법2

템플릿 리터럴은 + 연산자를 사용하지 않고 간단한 방법으로 문자열을 삽입할 수 있는데 이를 문자열 인터폴레이션(String Interpolation)이라고 한다.

1
2
3
4
5
6
7
const first = 'GildDong';
const last = 'Hong';

//ES5 String
console.log('My name is ' + first + ' ' + last + '.');
//문자열 인터폴레이션(String Interpolation)
console.log(`My name is ${first} ${last}.`);

문자열 인터폴레이션(String Interpolation)은 ${...} 으로 표현식을 감싸고 인터폴레이션 내의 표현식은 문자열로 강제 타입 변환된다.

1
console.log(`2 * 9 = ${2 * 9}`); //18

참조

https://poiemaweb.com/es6-template-literals

https://jsdev.kr/t/es6/2944

OAuth 2.0

OAuth 2.0은 OAuth 1.0의 단점을 개선한 것으로, OAuth 1.0은 웹 애플리케이션이 아닌 애플리케이션에서는 사용하기 곤란하다는 단점이 있다. 그리고 절차가 복잡하여 OAuth 구현 라이브러리를 제작하기 어렵고, 복잡한 절차 때문에 Service Provider에게도 연산 부담이 발생한다.

OAuth 1.0과 OAuth2.0의 차이

  • 인증절차의 간소화로 인해 개발자가 구현하기 쉬워짐
  • 기존에 사용하던 용어도 바뀌면서 Authorization Server와 Resource서버의 분리가 명시적으로 되었다.
  • 다양한 인증방식을 지원

OAuth 2.0의 특징

  1. 인증절차 간소화
    • 기능을 단순화했으며 기능과 규모의 확장성 등을 지원한다.
      • OAuth 1.0은 디지털 서명 기반이지만 OAuth 2.0의 암호화는 HTTPS에 맡김으로 복잡한 디지털 서명에 관한 로직을 요구하지 않으므로 개발이 쉬움 쉽게말해 암호화가 필요 없으며 HTTPS를 사용하고 HMAC을 사용하지 않는다.
  2. 용어변경
    • Resource Owner : 사용자 (1,0 User해당)
    • Resource Server : 자원을 호스팅 하는 서버[REST API Server(1.0 Protected Resource)]
    • Authorization Server : 사용자의 동의를 받아 권한을 부여하는 인증서버.(API 서버와 같을 수 있다. 1.0 Service Provider) 일반적으로 Resource Server와 같은 URL 하위에 있는 경우가 많다.
    • Client : Resource Server에서 제공하는 자원을 사용하는 애플리케이션[ Third Party Application(1.0 Service Provider해당)]
  3. Resource Server와 Authorization Server의 분리
    • 커다란 서비스는 인증 서버로 분리하거나 다중화 할 수 있어야한다.
    • Authorization Server의 역할을 명확히 한다.
  4. 다양한 인증방식(grant_type)

    • Authorization Code Grant
    • Implicit Grant
    • Resource Owner Password Credentials Grant
    • Client Credentials Grant
    • Device Code Grant
    • Refresh Token Grant
  5. 웹 애플리케이션이 아닌 데스크탑, 앱 애플리케이션 지원강화

  6. 그외

    • Signature의 단순화 정렬과 URL 인코딩이 필요 없다.
    • Access Token 갱신 Ouath 1.0에서 Access Token을 받으면 Access Token을 계속 사용할 수 있다. OAuth2.0에서는 보안 강화를 위해 Access Token의 Life-time을 지정할 수 있도록 했다.

인증 종류

OAuth 2.0은 6가지의 인증종류가 있다.

Authorization Code Grant

Authorization Code Grant는 일반적인 웹사이트에서 소셜로그인과 같은 인증을 받을 때 가장 많이 쓰는 방식으로 기본적으로 지원하고 있는 방식이다.

  • 1 Authorization Request : 클라이언트가 Redirect URL을 포함하여 Authorization server 인증 요청을 한다.
  • 2 User Login & Consent : Authorization Server는 유저에게 로그인창을 제공하여 유저를 인증하게 된다.
  • 3 Authorization Code Response : Authorization Server는 Authorization code를 클라이언트에게 제공해준다.
  • 4, 5, 6, 7, 8, 9 :Client는 코드를 Authorization server에 Access Token을 요청한다. 그리고 Authorization 서버는 클라이언트에게 Access token을 발급해주고, Access Token을 이용하여 Resource server에 자원을 접근할 수 있게 된다.
  • 10, 11 : 토큰이 만료된다면 Refresh token을 이용하여 토큰을 재발급 받을 수 있다.

Implicit Grant

Public Client인 브라우저 기반의 애플리케이션(Javascript application)이나 모바일 애플리케이션에서 바로 Resource Server에 접근하여 사용할 수 있는 방식이다.

  • 1 : 클라이언트는 Authorization server에 인증을 요청한다.
  • 2 : 유저는 Authorization server를 통해 인증한다.
  • 3 : Authorization server는 Access token을 포함하여 클라이언트의 Redirect URL을 호출한다.
  • 4 : 클라이언트는 해당 Access token이 유효한지 Authorization server에 인증요청한다.
  • 5 : 인증서버는 그 토큰이 유효하다면 토큰의 만기시간과함께 리턴해준다.
  • 6, 7 : 클라이언트는 Resource server에 접근할 수 있게된다.

Resource Owner Pasword Credentials Grant

Client에 아이디/패스워드를 받아 아이디/패스워드로 직접 access token을 받아오는 방식이다. Client가 신용이 없을 때에는 사용하기에 위험하다는 단점이 있다. 클라이언트가 확실한 신용이 보장될 때 사용할 수 있는 방식이다.

  • 1 : User가 Id와 Password를 입력한다
  • 1.1 : 클라이언트는 유저의 id와 password와 클라이언트 정보를 넘긴다.
  • 1.2 : Authorization sever는 Access token을 넘긴다.

Client Credentials Grant

애플리케이션이 Confidential Client일 때 id와 secret을 가지고 인증하는 방식이다.

  • 1 : 클라이언트 정보를 Authorization server에 넘긴다.
  • 2 : Access Token을 Client에 전달한다.

Device Code Grant

장치 코드 부여 유형은 브라우저가 없거나 입력이 제한된 장치에서 사용됩니다.

Refresh Token Grant

기존에 저장해둔 리프러시 토큰이 존재할 때 엑세스토큰 재발급 받을 필요가 있을 때 사용한다. 그리고 기존 액세스는 토큰이 만료된다.

참조

https://developers.payco.com/guide/development/start
https://d2.naver.com/helloworld/24942
https://minwan1.github.io/2018/02/24/2018-02-24-OAuth/
https://www.oauth.com/OAuth2-servers/differences-between-OAuth-1-2/
https://developer.accela.com/docs/construct-authCodeFlow.html

템플릿 리터럴(Template Literals)

ES6는 템플릿 리터럴(Template Literals)은 일반 문자열과 비슷해보이지만 ' 또는 " 같은 통상적인 따옴표 문자 대신 백틱(Backtick) (`) 문자를 사용한다.

1
2
3
const template = `템플릿 리터럴은 '작은따옴표(single quotes)'과 "큰따옴표(double quotes)"를 혼용할 수 있다.`;

console.log(template);

방법1

일반적인 문자열에서 줄바꿈은 허용되지 않고 공백(white-spce)를 표현하기 위해 백슬래시(\)로 시작하는 이스케이프 시퀀스(Escape Sequence)를 사용해야한다. 템플릿 리터럴은 일반적인 문자열과 다르게 여러 줄에 걸쳐 문자열을 작성할 수 있으며 템플릿 리터럴 내의 모든 white-space는 있는 그대로 적용된다.

1
2
3
4
5
6
7
8
const template = `<ul>
<li><a href="#home">Home</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul>`;

console.log(template);

[결과]

1
2
3
4
5
6
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#news">News</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#about">About</a></li>
</ul>

방법2

템플릿 리터럴은 + 연산자를 사용하지 않고 간단한 방법으로 문자열을 삽입할 수 있는데 이를 문자열 인터폴레이션(String Interpolation)이라고 한다.

1
2
3
4
5
6
7
const first = 'GildDong';
const last = 'Hong';

//ES5 String
console.log('My name is ' + first + ' ' + last + '.');
//문자열 인터폴레이션(String Interpolation)
console.log(`My name is ${first} ${last}.`);

문자열 인터폴레이션(String Interpolation)은 ${...} 으로 표현식을 감싸고 인터폴레이션 내의 표현식은 문자열로 강제 타입 변환된다.

1
console.log(`2 * 9 = ${2 * 9}`); //18

참조

https://poiemaweb.com/es6-template-literals

https://jsdev.kr/t/es6/2944

후위 표기식

문제

수식은 일반적으로 3가지 표기법으로 표현할 수 있다. 연산자가 피연산자 가운데 위치하는 중위 표기법(일반적으로 우리가 쓰는 방법이다), 연산자가 피연산자 앞에 위치하는 전위 표기법(prefix notation), 연산자가 피연산자 뒤에 위치하는 후위 표기법(postfix notation)이 그것이다. 예를 들어 중위 표기법으로 표현된 a+b는 전위 표기법으로는 +ab이고, 후위 표기법으로는 ab+가 된다.

이 문제에서 우리가 다룰 표기법은 후위 표기법이다. 후위 표기법은 위에서 말한 법과 같이 연산자가 피연산자 뒤에 위치하는 방법이다. 이 방법의 장점은 다음과 같다. 우리가 흔히 쓰는 중위 표기식 같은 경우에는 덧셈과 곱셈의 우선순위에 차이가 있어 왼쪽부터 차례로 계산할 수 없지만 후위 표기식을 사용하면 순서를 적절히 조절하여 순서를 정해줄 수 있다. 또한 같은 방법으로 괄호 등도 필요 없게 된다. 예를 들어 a+bc를 후위 표기식으로 바꾸면 abc+가 된다.

중위 표기식을 후위 표기식으로 바꾸는 방법을 간단히 설명하면 이렇다. 우선 주어진 중위 표기식을 연산자의 우선순위에 따라 괄호로 묶어준다. 그런 다음에 괄호 안의 연산자를 괄호의 오른쪽으로 옮겨주면 된다.

예를 들어 a+bc는 (a+(bc))의 식과 같게 된다. 그 다음에 안에 있는 괄호의 연산자 를 괄호 밖으로 꺼내게 되면 (a+bc)가 된다. 마지막으로 또 +를 괄호의 오른쪽으로 고치면 abc*+가 되게 된다.

다른 예를 들어 그림으로 표현하면 A+B*C-D/E를 완전하게 괄호로 묶고 연산자를 이동시킬 장소를 표시하면 다음과 같이 된다.

이러한 사실을 알고 중위 표기식이 주어졌을 때 후위 표기식으로 고치는 프로그램을 작성하시오

입력

첫째 줄에 중위 표기식이 주어진다. 단 이 수식의 피연산자는 A~Z의 문자로 이루어지며 수식에서 한 번씩만 등장한다. 그리고 -A+B와 같이 -가 가장 앞에 오거나 AB와 같이 가 생략되는 등의 수식은 주어지지 않는다. 표기식은 알파벳 대문자와 +, -, , /, (, )로만 이루어져 있으며, 길이는 100을 넘지 않는다.

출력

첫째 줄에 후위 표기식으로 바뀐 식을 출력하시오

예제 입력 1

1
A*(B+C)

예제 출력 1

1
ABC+*

풀이

스택을 이용해서 문제를 해결할 수 있다.
이 문제는 괄호가 있거나 연산자의 우선순위가 높을경우 라는 경우의 수가 있다.

아래 코드를 통해서 연산자 우선순위를 지정해준다.

1
2
3
4
5
6
7
8
public static int prec(char op) {
switch (op) {
case '(': case ')': return 0;
case '+': case '-': return 1;
case '*': case '/': return 2;
default: return -1;
}
}

그리고 여는괄호( 를 만나면 스택에 push하고 닫는 괄호 )를 만나면 여는 괄호를 만날 때까지 pop 해준다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Stack;

public class Main {


public static void main(String[] args) throws Exception {
convertToPostfix();
}

public static void convertToPostfix() throws Exception {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String infix = in.readLine();

Stack<Character> stack = new Stack<>();
for (char ch : infix.toCharArray()) {
switch (ch) {
case '+':case '-':case '*':case '/':
//스택에 있는 연산자가 우선순위가 더 크거나 같으면 출력
while(!stack.isEmpty() && prec(stack.peek()) >= prec(ch))
out.write(stack.pop());
stack.push(ch);
break;
case '(':
stack.push(ch);
break;
case ')':
char op = stack.pop();
//왼쪽괄호를 만날 때까지 계속 출력
while (op != '(') {
out.write(op);
op = stack.pop();
}
break;
default: //A~Z
out.write(ch);
break;
}
}
while(!stack.isEmpty()) {
out.write(stack.pop());
}
out.flush();
out.close();
in.close();
;
}

public static int prec(char op) {
switch (op) {
case '(': case ')': return 0;
case '+': case '-': return 1;
case '*': case '/': return 2;
default: return -1;
}
}
}

문제 사이트 : https://www.acmicpc.net/problem/6588

[BaekJoon-1935] 후위표기식2

문제

후위 표기식과 각 피연산자에 대응하는 값들이 주어져 있을 때, 그 식을 계산하는 프로그램을 작성하시오.

입력

첫째 줄에 피연산자의 개수(1 ≤ N ≤ 26) 가 주어진다. 그리고 둘째 줄에는 후위 표기식이 주어진다. (여기서 피연산자는 A~Z의 영대문자이며, A부터 순서대로 N개의 영대문자만이 사용되며, 길이는 100을 넘지 않는다) 그리고 셋째 줄부터 N+2번째 줄까지는 각 피연산자에 대응하는 값이 주어진다. (3번째 줄에는 A에 해당하는 값, 4번째 줄에는 B에 해당하는값 , 5번째 줄에는 C …이 주어진다, 그리고 피연산자에 대응 하는 값은 정수이다)

출력

계산 결과를 소숫점 둘째 자리까지 출력한다.

예제 입력 1

1
2
3
4
5
6
7
5
ABC*+DE/-
1
2
3
4
5

예제 출력 1

1
6.20

예제 입력 2

1
2
3
1
AA+A+
1

예제 출력 2

1
3.00

문제해결

스택을 이용하여 문제를 해결해나간다.

피연산자일 경우 스택에 Push하고 연산자를 만나면 두 수를 pop()하여 연산을 하고 연산결과를 다시 Push를 한다

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Stack;

public class Main {


public static void main(String[] args) throws Exception {
postfixNotation();
}

public static void postfixNotation() throws Exception {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

int n = Integer.parseInt(in.readLine());
String postfix = in.readLine();
double[] numbers = new double[n];
for (int i = 0; i < n; i++) {
numbers[i] = Double.parseDouble(in.readLine());
}

Stack<Double> stack = new Stack<>();
for (char ch : postfix.toCharArray()) {
if (ch >= 'A' && ch <= 'Z') {
stack.push(numbers[(ch - 'A')]);
} else {
double n2 = stack.pop();
double n1 = stack.pop();
switch (ch) {

case '+':
stack.push(n1 + n2);
break;
case '-':
stack.push(n1 - n2);
break;
case '*':
stack.push(n1 * n2);
break;
case '/':
stack.push(n1 / n2);
break;
}
}
}
out.write(String.format("%.2f\n", stack.pop()));
out.flush();
;
out.close();
in.close();
}
}

문제 사이트 : https://www.acmicpc.net/problem/1935

1. OAuth란?

개요

OAuth는 Open Authorization or Open Authentication이란 뜻으로 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹 사이트 상의 자신들의 정보에 대해 웹 사이트나, 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로 사용되는 접근 위임을 위한 개방형 표준이다. 구글, 페이스북, 트위터, 카카오, 네이버 등의 기업들이 타사 애플리케이션이나 웹 사이트의 계정을 공유할 수 있게 허용해준다.

애플리케이션 서비스 공급자들이(Service Provider) 유저의 비밀번호를 Third Party앱에 제공 없이 인증, 인가를 할 수 있는 표준 프로토콜이며, OAuth 인증을 하면 애플리케이션 API를 접근할 수 있는 권한을 얻을 수 있다.

과거에는 인증방식의 표준이 없기에 아이디와 비밀번호를 사용했고 이것은 보안상 취약한 구조를 가지고 있었다. 유저의 비밀번호가 노출될 가능성이 크기 때문이다. 이 문제를 해결하기 위해 OAuth의 인증이 등장했고 API를 제공하는 서버에서 인증을 진행하고 유저가 인증되었으면 Access Token을 발급하였다. 발급이 완료된 Access Token은 Third Party(Consumer) 애플리케이션에서 서비스 공급자(Service Provider)의 API를 안전하고 쉽게 사용할 수 있게 되었다.

용어

용어 설명
사용자(User) Service Provider에 계정을 가지고 있으면서, Consumer 앱을 사용하려는 사용자
서비스 공급자(Service Provider) OAuth를 통해 접근을 지원하는 웹 애플리케이션(Open API를 제공하는 서비스. ex) kakao, naver, facebook, google, etc.)
보호 자원(Protected Resource) 서비스 공급자(Service Provider)로부터 제공되어지는 API 자원들
소비자(Consumer) Open API를 이용하여 개발된 OAuth를 사용하여 서비스 공급자의 기능을 사용하는 웹 사이트 또는 애플리케이션
소비자 키(Consumer Key) 소비자(Consumer)가 서비스 제공자(Service Provider)에게 자신임을 식별하는데 사용하기 위한 키
소비자 비밀번호(Consumer Secret) 소비자(Consumer)가 소유권을 인정하기 위해 소비자(Consumer)가 사용하는 Secret
요청 토큰(Request Token) 소비자가 사용자에게 접근권한을 인증받기 위해 필요한 정보가 담겨 있으며 후에 접근 토큰(Access token)으로 변환
접근 토큰(Access Token) 인증 후 사용자가 서비스 제공자가 아닌 소비자를 통해서 보호된 자원에 접근하기 위한 키를 포함한 값
토큰 암호(Token Secret) 주어진 토큰의 소유권을 인증하기 위해 소비자가 사용하는 Secret

OAuth 1.0의 WorkFlow

사전에 소비자(Consumer)는 서비스 공급자(Service Provider)로부터 Client Key와 Secret을 발급 받아야한다. 이것은 서비스 공급자(Service Provider)의 API를 사용할 것을 등록하는 것과 동시에 서비스 공급자(Service Provider)가 소비자(Consumer)을 식별할 수 있게 한다.

처리과정

  1. Consumer는 Request Token을 받기위해 Consumer 정보, Signature 정보를 A 처럼 Service Provider에 보내어 Request Token을 요청하고 Service Provider는 Request Token을 발급하여 B 처럼 Consumer에게 결과를 보낸다.

  2. Request Token을 받은 Consumer는 C 처럼 Service Provider의 인증 사이트로 이동하여 유저는 그곳에서 Service Provider의 유저임을 인증한다.

  3. 그러면 Service Provider는 D처럼 유저 인증이되면 Consumer에게 OAuth_token과 OAuth_verifier를 넘겨준다.
  4. Consumer는 OAuth_token과 OAuth_verifier를 받고 E의 흐름처럼 다시 서명을 하여 Service Provider는 Access Token을 생성을 한다.
  5. 그리고 F처럼 다시 Consumer에게 Access Token을 전송하고 Accsss Token을 가진 Consumer는 Access Token 및 서명정보를 통해 G 처럼 Service Provider의 Protected Resource에 접근할 수 있다.

Request Token 요청 매개변수

매개변수 설명
oauth_callback Service Provider가 인증을 완료한 후 리다이렉트할 Consumer의 웹 주소. 만약 Consumer가 웹 애플리케이션이 아니라 리다이렉트할 주소가 없다면 소문자로 ‘oob’(Out Of Band라는 뜻)를 값으로 사용한다.
oauth_consumer_key Consumer를 구별하는 키 값. Service Provider는 이 키 값으로 Consumer를 구분한다.
oauth_nonce Consumer에서 임시로 생성한 임의의 문자열. oauth_timestamp의 값이 같은 요청에서는 유일한 값이어야 한다. 이는 악의적인 목적으로 계속 요청을 보내는 것을 막기 위해서이다.
oauth_signature OAuth 인증 정보를 암호화하고 인코딩하여 서명 값. OAuth 인증 정보는 매개변수 중에서 oauth_signature를 제외한 나머지 매개변수와 HTTP 요청 방식을 문자열로 조합한 값이다. 암화 방식은 oauth_signature_method에 정의된다.
oauth_signature_method oauth_signature를 암호화하는 방법. HMAC-SHA1, HMAC-MD5 등을 사용할 수 있다.
oauth_timestamp 요청을 생성한 시점의 타임스탬프. 1970년1월 1일 00시 00분 00초 이후의 시간을 초로 환산한 초 단위의 누적 시간이다.
oauth_version OAuth 사용버전. 1.0a는 1.0이라고 명시하면 된다.

oauth_signature 만들기

OAuth 1.0에서는 Service Provider에게 요청을 하려면 매번 oauth_signature 생성해야 한다. Consumer와 Service Provider가 같은 암호화(signing) 알고리즘을 이용하여 oauth_signature 만들어야 한다.

oauth_signature 다음과 같이 네 단계를 거쳐 만든다.

  1. 요청 매개변수를 모두 모은다.
    oauthsignature를 제외하고 ‘oauth‘로 시작하는 OAuth 관련 매개변수를 모은다. POST body에서 매개변수를 사용하고 있다면 이 매개변수도 모아야 한다.

  2. 매개변수를 정규화(Normalize)한다.
    모든 매개변수를 사전순으로 정렬하고 각각의 키(key)와 값(value)에 URL 인코딩(rfc3986)을 적용한다. URL 인코딩을 실시한 결과를 = 형태로 나열하고 각 쌍 사이에는 &을 넣는다. 이렇게 나온 결과 전체에 또 URL 인코딩을 적용한다.

  3. Signature Base String을 만든다.
    HTTP method 명(GET 또는 POST), Consumer가 호출한 HTTP URL 주소(매개변수 제외), 정규화한 매개변수를 ‘&’를 사용해 결합한다. 즉 ‘[GET|POST] + & + [URL 문자열로 매개변수는 제외] + & + [정규화한 매개변수]’ 형태가 된다. 이 예제에서는 ‘http://nid.naver.com/naver.oauth‘ 을 URL로 사용하고, 이 URL에 URL 인코딩을 적용한 값을 사용했다.

  4. 키 생성
    3번 과정까지 거쳐 생성한 문자열을 암호화한다. 암호화할 때 Consumer Secret Key를 사용한다. Consumer Secret Key는 Consumer가 Service Provider에 사용 등록을 할 때 발급받은 값이다. HMAC-SHA1 등의 암호화 방법을 이용하여 최종적인 oauth_signature를 생성한다.

Access Token 생성 요청

Access Token을 요청하는 방법은 Request Token을 요청하는 방법과 거의 같지만, 사용하는 매개변수의 종류가 약간 다르고 oauth_signature를 생성할 때 사용하는 키가 다르다.

  • Access Token 을 요청할 때에는 매개변수 oauth_callback는 없고, oauth_token와 oauth_verifer가 있다.
  • Request Token 발급을 요청할 때에는 Consumer Secret Key를 사용해 oauth_token_secret를 생성했다.
  • Access Token 발급을 요청할 때에는 Consumer Secret Key에 oauth_token_secret을 결합한 값(Consumer Secret Key + & + oauth_token_secret)을 사용해 oauth_token_secret를 생성한다. 암호화 키를 변경하여 보안을 더 강화하는 것이다.

Access Token발급 요청 매개변수

매개변수 설명
oauth_consumer_key Consumer를 구별하는 키 값. Service Provider는 이 키 값으로 Consumer를 구분한다.
oauth_nonce Consumer에서 임시로 생성한 임의의 문자열. oauth_timestamp의 값이 같은 요청에서는 유일한 값이어야 한다. 이는 악의적인 목적으로 계속 요청을 보내는 것을 막기 위해서이다.
oauth_signature OAuth 인증 정보를 암호화하고 인코딩하여 서명 값. OAuth 인증 정보는 매개변수 중에서 oauth_signature를 제외한 나머지 매개변수와 HTTP 요청 방식을 문자열로 조합한 값이다. 암화 방식은 oauth_signature_method에 정의된다.
oauth_signature_method oauth_signature를 암호화하는 방법. HMAC-SHA1, HMAC-MD5 등을 사용할 수 있다.
oauth_timestamp 요청을 생성한 시점의 타임스탬프. 1970년1월 1일 00시 00분 00초 이후의 시간을 초로 환산한 초 단위의 누적 시간이다.
oauth_version OAuth 사용 버전
oauth_verifier Request Token 요청 시 oauth_callback으로 전달받은 oauth_verifier 값
oauth_token Request Token 요청 시 oauth_callback으로 전달받은 oauth_token 값

위의 표에 정의한 매개변수를 상황에 맞게 정의한 다음 Access Token을 요청하면 oauth_token과 oauth_token_secret을 전달받게 된다. Service Provider에 따라 사용자의 아이디나 프로필 정보 같은 것들이 반환되기도 한다.

Acess Token 사용

매개변수 설명
oauth_consumer_key Consumer를 구별하는 키 값. Service Provider는 이 키 값으로 Consumer를 구분한다.
oauth_nonce Consumer에서 임시로 생성한 임의의 문자열. oauth_timestamp의 값이 같은 요청에서는 유일한 값이어야 한다. 이는 악의적인 목적으로 계속 요청을 보내는 것을 막기 위해서이다.
oauth_signature OAuth 인증 정보를 암호화하고 인코딩하여 서명 값. OAuth 인증 정보는 매개변수 중에서 oauth_signature를 제외한 나머지 매개변수와 HTTP 요청 방식을 문자열로 조합한 값이다. 암화 방식은 oauth_signature_method에 정의된다.
oauth_signature_method oauth_signature를 암호화하는 방법. HMAC-SHA1, HMAC-MD5 등을 사용할 수 있다.
oauth_timestamp 요청을 생성한 시점의 타임스탬프. 1970년1월 1일 00시 00분 00초 이후의 시간을 초로 환산한 초 단위의 누적 시간이다.
oauth_version OAuth 버전
oauth_token oauth_callback으로 전달받은 oauth_token

참조

https://ko.wikipedia.org/wiki/OAuth
https://oauth.net/core/1.0/