안경잡이개발자

728x90
반응형

문제 유형: 그리디

문제 URL: https://codeforces.com/contest/915/problem/C


  이 문제는 특정한 수가 주어졌을 때 그 수로 순열을 처리하는 문제입니다. 두 수 a와 b가 있다고 합시다. 이 때 a로 만들 수 있는 자릿수의 순열 중에서 b보다 크지 않은 가장 큰 수를 구해 출력하는 문제입니다. 입력과 출력으로는 0으로 시작하지 않는 양의 정수가 입력 됩니다. 이 때 자릿수의 순열이란, 수에서 숫자들의 자리를 서로 바꾸어 나타낼 수 있는 수들을 의미합니다.


  b보다 크지 않은 가장 큰 수를 구해야 하므로 맨 왼쪽부터 가장 큰 숫자가 나올 수 있도록 하나씩 숫자를 배정하면 문제를 해결할 수 있습니다. 이 때 가장 중요한 것은 단순히 들어갈 수 있는 가장 큰 숫자를 넣는다고 되지는 않습니다. 그렇게 하면 답을 못 구할 수도 있어요. 숫자를 하나씩 넣을 때마다 최소 크기 접미사를 붙여서 b보다 작은지 검사하는 로직이 추가적으로 필요합니다.


  a: 123456789123456789

  b: 276193619183618162


  왜 최소 크기 접미사를 구해야 하는지는 위의 예시를 통해 확인할 수 있습니다.


  단순히 들어갈 수 있는 가장 큰 숫자를 계속해서 배정하면 '276193619'에서 더이상 진행할 수 없습니다. 그 뒤에 오는 b의 숫자가 1이므로, 문자열 a에 포함된 숫자 중에서 넣을 수 있는 1보다 작거나 같은 숫자는 더 이상 존재하지 않기 때문이에요. 그러므로 9 대신 그 다음 작은 숫자인 8이 들어가서 '2761936189~'로 진행되는 방식으로 처리해야 합니다. 이를 효과적으로 판단하기 위해서 최소 크기 접미사를 넣어보는 겁니다.


  9를 넣는 경우와 8을 넣는 경우를 비교해봅시다. 왼쪽의 수는 우리가 찾을 해답이고, 오른쪽에 있는 수는 b를 의미합니다.


  276193619234455788 > 276193619183618162 (불가능)

  276193618234455789 < 276193619183618162 (가능)


  9를 넣는 경우와 8을 넣는 경우 각각에 대해서 최소 접미사를 붙인 경우는 위와 같습니다. 9가 들어가는 경우에는 최소 크기 접미사를 붙였을 때, b보다 더 커지므로 불가능한 것입니다. 반면에 그보다 더 작은 숫자인 8부터 다시 넣어서 최소 크기 접미사를 붙여보니 b보다 작거나 같으므로 8은 들어갈 수 있는 것입니다. 결과적으로 기본적으로 왼쪽부터 최대한 큰 숫자를 채워 넣고, 해당 숫자가 들어갈 수 있는 지의 여부는 최소 접미사를 통해 확인할 수 있는 것입니다.


※ 예제 입출력 ※

input
Copy
123
222
output
Copy
213
input
Copy
3921
10000
output
Copy
9321
input
Copy
4940
5000
output
Copy
4940


※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>
#define MAX 10

using namespace std;

string a, b;
int d[MAX];
string res;

// 남은 뒷 자릿들을 '가장 작은 수'가 되도록 채웁니다. 
void makeSmall(int from)
{
	for (int i = 0; i < MAX; i++)
		for (int j = 0; j < d[i]; j++)
			res[from++] = i + '0';
}

int main()
{
	cin >> a;
	cin >> b;
	for (int i = 0; i < a.length(); i++)
		d[a[i] - '0']++;
	int zeros = 0;
	// A가 B보다 자릿수가 작은 경우 앞에 0을 채웁니다. 
	while (a.length() < b.length()) {
		a = "0" + a;
		zeros++;
	}
	res = a;
	// '원래 A의 길이'만큼 반복합니다. 
	for (int i = zeros; i < a.length(); i++) {
		// 9부터 0까지의 숫자 중에서 들어갈 수 있는 것을 고릅니다. 
		for (int j = MAX - 1; j >= 0; j--) {
			// 일단 넣어 봅니다. 
			if (d[j] > 0) {
				d[j]--;
				res[i] = j + '0';
				// 현재 넣은 숫자를 토대로 뒷자리들은 최대한 작게 구성합니다.
				// 이렇게 만들어진 수가 가능한 수라면 break;하여 해당 자릿수 구성을 완료합니다. 
				makeSmall(i + 1);
				cout << i << ":" << res << '\n';
				if (res <= b) break;
				d[j]++;
			}	
		}
	}
	// 앞에 들어가는 0은 무시하고 출력합니다. 
	int pos = 0;
	while (res[pos] == '0') pos++;
	cout << res.substr(pos);
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: https://codeforces.com/contest/1075/problem/B


  끝 없이 이어지는 좌표 상의 선으로 구성된 도시가 있습니다. 이 중에서 매일 도시 거주자들을 태워주는 M명의 택시 기사가 있습니다. 전체 구성원은 N명입니다. 택시 기사를 포함해 모든 N명의 사람들은 고유한 위치에 거주하고 있습니다.


  어떠한 사람이 택시를 부르면, 모든 택시 기사에게 전화를 하는 것이 아니라 자신과 가장 가까운 택시 기사 한 명만 부르게 됩니다. 만약에 거리가 동일한 택시기사가 여러 명이라면, 좌표 상에서 가장 작은 위치를 가진 택시 기사를 부르게 됩니다.


  어느 날 아침에, 택시 기사들은 하나의 의문이 생겼습니다. "매일 각 택시 기사들은 몇 명에게 전화를 받을까?"입니다. 이에 대한 답을 알려주는 프로그램을 작성하면 됩니다.


  예를 들어 일반 주민이 3명, 택시기사가 3명이라고 해봅시다. 이러면 전체 사람이 6명이므로 각각 위치가 주어지고, 각 위치에 따라서 해당 사람이 택시기사라면 1을, 아니라면 0이도록 입력이 주어지는 것입니다.


  전체 사람들의 위치: 2 3 4 5 6 7

  택시 기사들의 위치: 1 0 1 0 1 0


  이 때 택시 기사 3명은 각각 1명씩 전화를 받게 됩니다. 3번 위치의 사람은 2번 위치의 택시 기사에게, 5번 위치의 사람은 4번 위치의 택시 기사에게, 7번 위치의 사람은 6번 위치의 택시 기사에게 말입니다.


  이 문제는 간단히 크기가 2인 슬라이딩 윈도우를 이용해 풀 수 있습니다. 택시기사를 앞에서부터 두 명씩 묶어서 확인하면 돼요. 모든 사람들에 대해서 한 명씩 확인을 하는 건데요. 왼쪽 기사와 오른쪽 기사 사이에 있는 사람은, 더 가까운 기사에게 전화를 걸도록 처리하면 됩니다. 더 뒤쪽에 있는 택시기사의 위치보다 현재 사람의 위치가 크다면 슬라이딩을 수행하여, 그 이후의 택시기사 2명을 보는 방식을 사용합니다.


※ 입출력 예시 ※


input
Copy
3 1
1 2 3 10
0 0 1 0
output
Copy
3 
input
Copy
3 2
2 3 4 5 6
1 0 0 0 1
output
Copy
2 1 
input
Copy
1 4
2 4 6 10 15
1 1 1 1 0
output
Copy
0 0 0 1 

※ 정답 소스코드 ※
(컴파일 환경: GNU G++11 5.1.0)
#include <iostream>
#include <vector>

using namespace std;

vector a;
vector taxi;
vector res;

int main(void) {
	int n, m;
	cin >> n >> m;
	for(int i = 0; i < n + m; i++) {
		int x;
		cin >> x;
		a.push_back(x);
	}
	for(int i = 0; i < n + m; i++) {
		int x;
		cin >> x;
		if(x == 1) {
			taxi.push_back(a[i]);
			res.push_back(-1); // 택시 기사도 주민으로 보므로 초기에 -1로 설정 
		}
	}
	// 택시 기사가 1명이면, 크기가 2인 윈도우를 만들 수 없어 바로 답 출력 후 종료 
	if(taxi.size() == 1) {
		cout << a.size() - 1;
		return 0;
	}
	int now = 0;
	int start = taxi[now];
	int end = taxi[now + 1];
	for(int i = 0; i < a.size(); i++) {
		while(a[i] > end) {
			// 범위 한 칸 슬라이드 
			now++;
			// 마지막 범위인 경우 
			if(now == taxi.size() - 1) {
				// 남은 주민들을 마지막 택시에게 배정하고 종료 
				res[now] += a.size() - i;
				break;
			}
			start = taxi[now];
			end = taxi[now + 1];
		}
		if(now == taxi.size() - 1) break;
		// 현재 주민이 왼쪽과 가까운지, 오른쪽과 가까운지 판별 
		if(a[i] - start <= end - a[i]) {
			res[now]++;
		}
		else {
			res[now + 1]++;
		}
	}
	for(int i = 0; i < res.size(); i++) {
		cout << res[i] << ' ';
	}
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: https://codeforces.com/contest/1075/problem/A


  N x N의 체스 판이 있을 때 (1, 1)에 '화이트 킹'이 있고, (N, N)에 '블랙 킹'이 있습니다. 각 킹은 인접한 8가지 방향의 셀 중 한 곳으로 이동할 수 있습니다. 대각선도 포함해 이동할 수 있는 것입니다. 이 때 (X, Y)의 위치에 화이트 킹과 블랙 킹 중에서 누가 더 먼저 도착할 수 있는지를 구하면 되는 문제입니다. 다만 화이트 킹이 먼저 이동하며 시작과 동시에 킹의 위치와 (X, Y)가 동일하다면 그 즉시 종료됩니다.


  이 문제는 매우 간단한 문제입니다. (1, 1)에 화이트 킹이 있으며 (N, N)에 블랙 킹이 있으므로, 두 위치에서 (X, Y) 까지의 이동 횟수를 구하면 됩니다. 횟수를 구할 때는 각 킹들이 대각선 방향으로도 이동할 수 있다는 점에서 '가로 길이와 세로 길이 중에서 더 긴 것'을 구하면 됩니다.


  이동 횟수: 가로 길이와 세로 길이 중에서 더 긴 것


  결과적으로 이동 횟수를 비교하여 정답을 출력하면 됩니다.


※ 입출력 예시 ※


input
Copy
4
2 3
output
Copy
White
input
Copy
5
3 5
output
Copy
Black
input
Copy
2
2 2
output
Copy
Black


※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>
#include <stdio.h>

using namespace std;

int main(void) {
	long long int n;
	cin >> n;
	long long int x, y;
	cin >> x >> y;
	long long int black = max(n - x, n - y);
	long long int white = max(x - 1, y - 1);
	if(black == 0) {
		cout << "Black";
	}
	else if(white == 0) {
		cout << "White";
	}
	else if(white <= black) {
		cout << "White";
	}
	else {
		cout << "Black";
	}
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: https://codeforces.com/contest/915/problem/B


  브라우저에 N개의 탭이 열려 있습니다. 각 탭은 차례대로 1번부터 N번에 해당합니다. 현재 마우스 커서는 pos 번 탭에 존재하며 공부하기 위해서 l부터 r까지의 탭을 제외한 나머지 탭은 모두 꺼버리고 싶습니다. 다만 최대한 빠르게 탭을 정리하고자 합니다.


  매 초마다 커서를 왼쪽 혹은 오른쪽으로 이동할 수 있습니다. 물론 이동할 때 이미 꺼진 탭 번호로는 이동할 수 없습니다. 예를 들어 1, 2, 3번 탭을 끈 이후에 4번에서 그 왼쪽인 3번 탭으로는 이동할 수 없는 것입니다. 또한 매 초마다 탭을 끌 수 있으며, 탭을 끌 때는 현재 커서가 위치한 탭에서 왼쪽에 있는 모든 탭을 모두 끄거나 오른쪽에 있는 탭을 모두 끄는 방법이 있습니다. 현재 1부터 M까지의 탭이 켜져 있고, 현재 커서가 pos번 탭에 위치한 상태에서 l부터 r까지의 탭을 제외한 나머지 탭을 모두 끄는데 걸리는 가장 빠른 시간을 구하면 되는 문제입니다.


  이 문제는 매우 간단한 구현 문제입니다. 일단 l부터 r까지의 탭을 제외한 다른 모든 탭을 꺼야 합니다. 탭을 끌 때는 왼쪽에 있는 모든 탭을 끄거나 오른쪽에 있는 모든 탭을 끄는 방법이 있으므로 왼쪽으로 가는 경우와 오른쪽으로 가는 경우 중에서 더 빠른 경우부터 끄면 됩니다.


  답을 구하기 위해서는 가장 먼저 l이 1보다 큰지 확인하여 왼쪽에 있는 탭을 끄러 갈 필요가 있는지 확인합니다. r이 n보다 작은지 또한 고려해야 합니다. 이후에 기본적으로 오른쪽부터 끈다고 가정합니다. 결과적으로 왼쪽 탭 끄는 위치와 오른쪽 탭 끄는 위치 중에서 더 가까운 곳으로 이동하여 먼저 탭을 끈 뒤에 남은 탭으로 이동해서 끄면 됩니다.


※ 예시 입출력 ※


input
Copy
6 3 2 4
output
Copy
5
input
Copy
6 3 1 3
output
Copy
1
input
Copy
5 2 1 5
output
Copy
0


※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream> #include <algorithm> #include <math.h> using namespace std; int main(void) { int n, pos, l, r; cin >> n >> pos >> l >> r; vector<int> v; int count = 0; // 오른쪽을 꺼야 하는 경우 if(r < n) { v.push_back(1); } // 왼쪽을 꺼야 하는 경우 if(l > 1) { v.push_back(0); } // 왼쪽부터 끄는 게 더 가까운지 if(pos - l <= r - pos) { sort(v.begin(), v.end()); // 왼쪽부터 끄도록 함 } for(int i = 0; i < v.size(); i++) { int direction = v[i]; int target = -1; if(direction == 0) { target = l; } if(direction == 1) { target = r; } count += abs(pos - target) + 1; pos = target; } cout << count; return 0; }


728x90
반응형

728x90
반응형

문제 유형: 수학

문제 URL: https://codeforces.com/contest/915/problem/A


  정원은 길이가 K인 선분(Segment) 형태로 표현됩니다. 또한 N개의 그릇에 물을 담아서 농작물에 물을 주려고 합니다. i번째 그릇은 한 시간에 Ai 길이의 연속적인 공간에 물을 줄 수 있도록 해줍니다. 이 때 한 번 물을 준 곳은 더 이상 주지 않으며 주인공은 하나의 그릇을 선택하여 가능한 빠르게 농사를 짓는 전체 공간에 물을 주고자 합니다. 이 때 주인공이 가장 적은 시간으로 정원에 물을 줄 때의 시간을 구하면 됩니다.


  이 문제는 길이가 K인 전체 정원에 물을 주어야 하며 남는 공간이 없어야 하고, 한 번 물을 준 곳은 더 이상 줄 수 없다는 점에서 단순히 약수를 판별하는 문제입니다. 주어진 그릇의 크기 중에서 K의 약수로 가장 큰 것을 고르면 됩니다. 따라서 K를 해당 약수로 나눈 값을 출력하도록 하면 정답 처리를 받을 수 있습니다. 또한 항상 하나 이상의 그릇은 정원의 모든 공간에 물을 줄 수 있도록 입력이 주어진다고 합니다.


※ 예시 입출력 ※

input
Copy
3 6
2 3 5
output
Copy
2
input
Copy
6 7
1 2 3 4 5 6
output
Copy
7


※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>
#include <limits.h>

using namespace std;

int main(void) {
	int n, k;
	cin >> n >> k;
	int res = INT_MIN;
	for(int i = 0; i < n; i++) {
		int x;
		cin >> x;
		if(k % x == 0) {
			res = max(res, x);
		}
	}
	cout << k / res;
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: https://codeforces.com/contest/390/problem/A


  이 문제는 대표적인 단순 구현 문제입니다. 이 문제는 초기에 101 X 101 이차원 격자에 여러 개의 알람 시계가 있다고 가정합니다. 이 때 가로 혹은 세로에 있는 알람을 모두 한 번에 끌 수 있다고 합니다. 가로 혹은 세로 라인을 선택하는 최소 횟수를 구하면 됩니다.


  다만 알람을 끌 때 모두 가로로 끄거나, 모두 세로로 끄거나 둘 중 하나만 선택해야 합니다. 예를 들어 2번 가로 줄의 알람을 모두 끄고, 3번 세로 줄의 알람을 모두 끄는 것은 안 됩니다. 여러 번 알람을 끌 때 항상 가로로만 끄거나 세로로만 꺼야 한다는 겁니다. 사실 이러한 조건 때문에 이 문제는 매우 쉬운 단순 구현 문제에요.


  만약에 가로, 세로를 번갈아가면서 선택하며 끌 수 있었다면 이분 매칭(Bipartite Matching) 문제로 분류되어 조금 더 어려워집니다. 하지만 이 문제는 반드시 가로 혹은 세로로만 꺼야 한다는 강력한 제약 조건이 붙어 있으므로 단순히 <가로로만 끌 때의 경우의 수>와 <세로로만 끌 때의 경우의 수> 중에서 더 작은 수를 계산하여 출력하기만 하면 됩니다.


※ 예시 입출력 ※


input

4
1 1
1 2
2 3
3 3

output

3

※ 정답 소스코드 ※

(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>
#include <stdio.h>
#include <limits.h>

using namespace std;

int a[101][101];

int main(void) {
	int n;
	cin >> n;
	for(int i = 0; i < n; i++) {
		int x, y;
		cin >> x >> y;
		a[x][y] = 1;
	}
	int res = INT_MAX;
	int count = 0;
	for(int i = 0; i < 101; i++) {
		for(int j = 0; j < 101; j++) {
			if(a[i][j]) {
				count++;
				break;
			}
		}
	}
	res = min(res, count);
	count = 0;
	for(int i = 0; i < 101; i++) {
		for(int j = 0; j < 101; j++) {
			if(a[j][i]) {
				count++;
				break;
			}
		}
	}
	res = min(res, count);
	cout << res;
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 구현

문제 URL: http://codeforces.com/contest/399/problem/A


  이 문제는 구현의 끝판왕 문제인 게시판 페이징(Paging) 문제입니다. 전체 페이지의 개수를 N, 현재 페이지를 P, 현재 페이지를 기준으로 왼쪽과 오른쪽에 몇 개의 페이지까지 표시할 지를 K라고 합니다. 이 때 << 3 4 (5) 6 7 >>와 같은 방식으로 페이징 처리를 하면 됩니다.


  왼쪽으로 페이지가 더 남았을 때는 "<<"를 출력하고, 오른쪽으로 페이지가 더 남았을 때는 ">>"를 출력합니다. 실제 게시판을 구현할 때도 비슷한 방식의 페이징 처리 알고리즘 구현해 본 경험이 있다는 점에서 재미있게 풀 수 있었습니다.


※ 예시 입출력 ※


input

17 5 2

output

<< 3 4 (5) 6 7 >> 

※ 정답 소스코드 ※
(컴파일 환경: GNU G++11 5.1.0)
#include <iostream>
#include <stdio.h>

using namespace std;

int main(void) {
	int n, p, k;
	cin >> n >> p >> k;
	// 왼쪽으로 이동이 가능한 경우 
	if(p - k > 1) {
		cout << "<< ";
	}
	for(int i = ((p - k > 1)? p - k : 1); i < p; i++) {
		cout << i << ' ';
	}
	// 현재 페이지를 출력 
	cout << "(" << p << ") ";
	for(int i = p + 1; i <= ((p + k < n)? p + k : n); i++) {
		cout << i << ' ';
	}
	// 오른쪽으로 이동이 가능한 경우 
	if(p + k < n) {
		cout << ">>";
	}
	return 0;
}


728x90
반응형

728x90
반응형

문제 유형: 수학

문제 URL: http://codeforces.com/contest/393/problem/A


  이 문제는 특정한 문자열이 주어졌을 때 그 문자열의 문자들을 재정렬해서 "nineteen"이라는 문자열을 몇 개나 만들 수 있는지 물어보는 문제입니다. 예를 들어 문자열 "xiineteenppnnnewtnee"이 주어졌을 때 이를 "xnineteenppnineteenw"와 같이 재정렬한다면 "nineteen" 문자열이 두 번 나오게 되는 것입니다. 따라서 위 경우 최대 2개 만들 수 있으므로 정답은 2입니다.


  또한 "nineteen"이라는 문자열의 개수를 셀 때는 전체 문자열을 앞에서부터 한 자씩 읽어 확인하므로 문자를 읽다가 건너 뛸 수 없습니다. 또한 문자 n은 끝과 시작이 이어질 수 있습니다. 예를 들어 "nineteenineteen"과 같이 나열하면 n의 개수가 5개여도 "nineteen"을 2번 찾을 수 있습니다.


  이 문제는 단순히 n, i, e, t 문자의 개수를 세서 풀 수 있습니다. "nineteen"은 n 3개, i 1개, e 3개, t 1개로 이루어집니다. 따라서 만들 수 있는 "nineteen"의 최대 개수를 구하기 위해서는 각 문자를 몇 개씩 가지고 있느냐를 기준으로 개수를 찾을 수 있을 것입니다. 특히 n은 끝과 시작이 이어질 수 있기 때문에 사실상 n은 2개만 있어도 하나의 "nineteen"을 구성할 수 있다고 고려해야 합니다.


※ 정답 소스코드 ※


(컴파일 환경: GNU G++11 5.1.0)

#include <iostream>
#include <stdio.h>
#include <limits.h>

using namespace std;

int main(void) {
	int nC = 0;
	int iC = 0;
	int eC = 0;
	int tC = 0;
	string input;
	cin >> input;
	for(int i = 0; i < input.size(); i++) {
		if(input[i] == 'n') nC++;
		else if(input[i] == 'i') iC++;
		else if(input[i] == 'e') eC++;
		else if(input[i] == 't') tC++;
	}
	int res = INT_MAX;
	if(nC > 3) {
		// n은 끝과 시작이 이어질 수 있습니다. 
		res = min(res, (nC - 1) / 2);
	}
	else {
		res = min(res, nC / 3);
	}
	res = min(res, iC);
	res = min(res, tC);
	res = min(res, eC / 3);
	cout << res;
	return 0;
}


728x90
반응형

728x90
반응형

  다이얼로그 플로우(Dialog Flow)에서 스몰 토크(Small Talk)는 다양한 일상적인 대화들을 포함하는 내장 에이전트(Prebuilt Agent)입니다. 따라서 이 스몰 토크를 이용하면 순식간에 대부분의 일상 대화를 에이전트에 담을 수 있습니다. 한 번 스몰 토크 에이전트를 만들어 보겠습니다.



  기본적으로 자신의 프로젝트의 언어를 새롭게 만들 스몰 토크 에이전트의 언어와 일치하도록 만듭니다. 이후에 'Small Talk'를 눌러서 스몰 토크 에이전트를 만들어주겠습니다.



  IMPORT 버튼을 누르면 바로 에이전트를 생성할 수 있습니다.




  스몰 토크 에이전트가 생성된 이후에는 다음과 같이 기본적인 인텐트(Intent)들이 형성되어 있는 것을 볼 수 있습니다.



  이제 여기에 추가적인 개인 인텐트 파일을 업로드 해서 완전한 형태의 챗봇(Chat Bot)을 하나 만들 수 있습니다.



  실제로 스몰 토크가 만들어 준 인텐트를 확인해보면 다음과 같이 안정적으로 기본 예문들이 들어와 있습니다.



  반면에 한국어(KO) 설정에서도 스몰 토크 에이전트를 이용할 수 있습니다. 한국어 프로젝트에서 Prebuilt Agents 탭에 들어가 보시면 다음과 같이 몇 개의 내장 기능들이 존재합니다. 영어에 비해서 상대적으로 적은 기능들이지만 스몰 토크는 포함이 되어 있네요.



  한글로 만든 스몰 토크 에이전트는 다음과 같이 기본적인 한글 질문들이 포함되어 있지만 한글 대답은 포함되어 있지 않으므로 본인이 직접 넣어주셔야 합니다.




728x90
반응형

728x90
반응형

  다이얼로그 플로우는 다양한 Intent를 JSON 형식의 파일에서 불러오는 기능을 제공합니다. 따라서 JSON 파일의 형식이 잘못되었거나, 다이얼로그 플로우에서 설정하고 있는 기본적인 형식을 지키지 못하면 오류 메시지가 출력됩니다.



  일단 위와 같이 장문의 오류가 발생하면 당황하지 말고 개발자 도구를 열어서 오류 메시지를 정확히 복사합니다.




  오류 메시지가 위와 같이 굉장히 많이 나왔네요.



  이를 위와 같이 텍스트 파일에 옮겨줍시다. 이제 오류가 발생하는 해당 파일들을 일일이 삭제할 수 있습니다.



  혹은 텍스트 파일에 담은 뒤에 파일 이름만 파이썬으로 추출할 수 있습니다. 다음과 같이 말이죠.



import os 


FIND_DIR="./error messages.txt"


def find_all(string,substring):

    length = len(substring)

    c=0

    indexes = []

    while c < len(string):

        if string[c:c+length] == substring:

            indexes.append(c)

        c=c+1

    return indexes


f = open(FIND_DIR, "r")

s = f.read()

all_errors = find_all(s, "intents/")

for i in range(0, len(all_errors)):

  index = all_errors[i]

  # get only 12 characters

  print(s[index+8:index + 20])

f.close()


위 스크립트를 실행하면 정확히 오류가 발생하는 파일의 이름 앞 부분을 구할 수 있습니다.



  이제 이러한 파일들을 삭제해야 하는데요. 만약 삭제해야 할 파일의 개수가 많다면, 다음과 같이 파이썬 스크립트를 이용해서 일괄 삭제할 수도 있습니다. 저는 다음과 같이 삭제하고자 하는 파일을 배열에 하나씩 담아서 하나씩 순회하면서 삭제되도록 만들었습니다.


import os 


DIR="./Dialog Flow Example Project/intents/"


# deleting some intents that have an error sententce.

targets=[

'145127b6428c',

'14015d374076',

'1401cc8a40b3',

'1451bc4266be',

'1451b39a0148',

'105137cdd164',

'1401fec0f8e8',

'1051e6b4632a',

'145138afb780',

'1451bc4266be',

'1451e8f0d5cb',

'1051b5472e66',

'1001bb2b5200',

'1451153fae0e',

'1451bc0aa2a9',

'1001956e5832',

'105112bdd48a',

'1451bcdef726',

'1451ef55eba6',

'1051b3439dd6',

'1051bb288765',

'1001d0245578',

'14011ae6df96',

'1101cab5bc06',

'145103636150',

'140141837fb0',

'1401c7dde1b5',

'140192c0fdf1',

'1451400f135b'

]


for ROOT, DIRS, FILES in os.walk(DIR): 

  for F in FILES: 

    filename = "{}/{}".format(ROOT, F) 


    for i in range(0, len(targets)):

      if targets[i] in filename: 

        print("Deleting file \"{}\"".format(filename)) 

        os.remove(filename) 



  다만 위와 같이 하면 수동으로 파일명을 입력해야 하므로, 오류 메시지로부터 자동으로 파일 이름을 읽어서 오류가 발생하는 파일만 삭제하도록 스크립트를 합칠 수 있습니다.


import os 


FIND_DIR="./error messages.txt"

ERROR_DIR="./Dialog Flow Example Project/intents/"


def find_all(string,substring):

    length = len(substring)

    c=0

    indexes = []

    while c < len(string):

        if string[c:c+length] == substring:

            indexes.append(c)

        c=c+1

    return indexes


f = open(FIND_DIR, "r")

s = f.read()

all_errors = find_all(s, "intents/")


for ROOT, DIRS, FILES in os.walk(ERROR_DIR): 

  for F in FILES: 

    filename = "{}/{}".format(ROOT, F) 


    for i in range(0, len(all_errors)):

      index = all_errors[i]

      if s[index+8:index + 20] in filename: 

        print("Deleting file \"{}\"".format(filename)) 

        os.remove(filename)



  결과적으로 특정 인텐트의 JSON 파일에 오류가 나오는 경우 오류 메시지를 error message.txt에 기록만 해주고, 이 스크립트를 실행하면 알아서 오류 메시지에 적힌 오류 인텐트 파일들을 찾아서 그 파일들만 intents 폴더에서 지워주게 됩니다.



  결과적으로 위와 같이 더이상 문법과 관련한 오류는 나오지 않는 것을 알 수 있습니다.

728x90
반응형