[사이클 제거]
https://programmers.co.kr/learn/courses/30/lessons/49188
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
* 조건
- 1~N까지의 노드가 존재한다
- 노드간의 연결된 간선(edges[][])가 주어진다 (무방향)
- 주어진 그래프에서는 반드시 하나 이상의 사이클이 존재한다
- 노드를 딱 하나 제거해서 그래프가 사이클이 없도록 만들 수 있다면 해당 노드의 번호를 누적한 합을 출력한다
* 로직
- 효율성을 만족시키지 못했다. 일단은 정확성을 만족한 로직에 대해 먼저 작성하고 다시 시도해보려한다
- 크루스칼 알고리즘을 사용한다
- 가중치는 모두 1이라고 생각하고, 노드를 1~N까지 하나씩 뺐을 때의 경우를 고려한다
- 만약 크루스칼 알고리즘 진행중에 사이클이 형성된 곳이 없다면 -> 해당 노드를 누적한다
- 사이클이 형성되었다면 -> 다음번 노드를 제거한 경우를 고려한다
class Edge_RemoveCycle {
public int v1, v2;
public Edge_RemoveCycle(int v1, int v2) {
this.v1 = v1;
this.v2 = v2;
}
}
public class Problem_RemoveCycle {
static int[] parent;
public static void main(String[] args) {
int n = 8;
int[][] edges = {
// {1,2}, {1,3}, {2,3}, {2,4}, {3,4}
{1,2}, {2,3}, {3,4}, {4,5}, {5,6},{6,7},{7,8},{8,1},{2,7},{3,6}
};
int answer = solution(n, edges);
System.out.println(answer);
}
public static int solution(int n, int[][] edges) {
int answer = 0;
Edge_RemoveCycle[] edgesInfo = new Edge_RemoveCycle[edges.length];
for(int i=0; i<edges.length; ++i){
edgesInfo[i] = new Edge_RemoveCycle(edges[i][0], edges[i][1]);
}
//노드 1~n까지 하나씩 제거했을 때 사이클이 생기는지 안생기는지
for(int node=1; node<=n; ++node){
initParent(n);
boolean find = false;
for(Edge_RemoveCycle edge : edgesInfo){
if(edge.v1 == node || edge.v2 == node) continue;
if(!isSameParent(edge.v1, edge.v2)){
union(edge.v1, edge.v2);
}
else {
find = true;
break;
}
}
if(!find) {
answer += node;
}
}
return answer;
}
private static boolean isSameParent(int v1, int v2) {
v1 = findParent(v1);
v2 = findParent(v2);
if(v1 == v2) return true;
return false;
}
private static int findParent(int v) {
if(parent[v] == v) return v;
return parent[v] = findParent(parent[v]);
}
private static void union(int v1, int v2) {
v2 = findParent(v2);
parent[v2] = v1;
}
private static void initParent(int n) {
parent = new int[n+1];
for(int i=1; i<=n; ++i){
parent[i] = i;
}
}
}
[가사 검색]
https://programmers.co.kr/learn/courses/30/lessons/60060
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
* 조건
- 가사가 주어진다(words)
- 검색 키워드가 주어진다(queries)
- 오직 알파벳 소문자로만 구성되어있다
- queries에는 와일드 카드인 ?가 존재한다 (접두사 or 접미사로 구성된다)
- 와일드 카드가 포함된 queries에 포함되는 가사의 개수를 출력한다
* 알고리즘
- 트라이 알고리즘
* 로직
- 주어진 가사를 트라이 형태로 구성한다
- 같은 글자 수의 문자들을 하나의 트라이로 만든다 (정방향)
- 문자를 넣어줄 때, 각 글자마다 사용된 횟수를 카운팅한다
- 와일드 카드(?)가 접두사로 사용된 경우를 고려해 거꾸로 뒤집은 문자를 보관하는 트라이를 구성한다 (역방향)
(위에서 만든 트라이와 같은 방식이다)
- 주어진 검색 키워드가
- 끝 글자가 ? 라면 접미사로 사용된 경우이다
=> 정방향 트라이에서 문자를 검색한다
=> 다음 글자가 ?가 나온다면 현재 위치한 글자가 사용된 횟수를 리턴한다
=> 만약 글자가 존재하지 않는다면 0을 리턴한다 - 그렇지 않다면 접두사로 사용된 경우이다
=> 역방향 트라이에서 문자를 검색한다
=> 다음 글자가 ?가 나온다면 현재 위치한 글자가 사용된 횟수를 리턴한다
=> 만약 글자가 존재하지 않는다면 0을 리턴한다 - 또한, 모든 검색 키워드가 ?인 경우를 고려해야한다
=> 끝 글자가 ?인 상황, 정방향 트라이를 탐색하는 과정에서 맨 첫 글자가 ?인 경우에는 root의 개수를 출력하면 현재 글자 수에 존재하는 모든 단어의 개수를 리턴해줄 수 있다
- 끝 글자가 ? 라면 접미사로 사용된 경우이다
- 답을 출력한다
import java.util.ArrayList;
class Trie_SearchLyrics {
Node_SearchLyrics root;
public Trie_SearchLyrics() {
this.root = new Node_SearchLyrics(' ');
root.usedCnt = 0;
}
public void insert(String words) {
Node_SearchLyrics current = root;
current.usedCnt++;
for(int i=0; i<words.length(); ++i){
char ch = words.charAt(i);
int index = ch - 'a';
if(current.child[index] == null){
current.child[index] = new Node_SearchLyrics(ch);
}
else {
current.child[index].usedCnt++;
}
current = current.child[index];
}
}
public int search(String query){
Node_SearchLyrics current = root;
for(int i=0; i<query.length(); ++i){
char ch = query.charAt(i);
int index = ch - 'a';
//?인 경우 -> 현재 위치에서 글자 개수 출력
if(ch == '?') return current.usedCnt;
//단어가 존재한다면 해당 단어로 이동
if(current.child[index] != null) current = current.child[index];
//query의 단어가 존재하지 않는다면 0을 리턴
else if(current.child[index] == null) return 0;
}
//? 표기 없이 전체 단어로 다 돌았을 때 -> 마지막 단어에서 리턴(이 문제에서는 상관 없음)
return current.usedCnt;
}
}
class Node_SearchLyrics {
public char word;
public Node_SearchLyrics[] child;
public int usedCnt;
public Node_SearchLyrics(char word) {
this.word = word;
this.child = new Node_SearchLyrics[26];
this.usedCnt = 1;
}
}
public class Problem_SearchLyrics {
static Trie_SearchLyrics[] tries = new Trie_SearchLyrics[10001];
static Trie_SearchLyrics[] tries_reversed = new Trie_SearchLyrics[10001];
public static void main(String[] args) {
String[] words = {"frodo", "front", "frost", "frozen", "frame", "kakao"};
String[] queries = {"fro??", "????o", "fr???", "fro???", "pro?"};
int[] answer = solution(words, queries);
for(int ans : answer)
System.out.print(ans + " ");
}
public static int[] solution(String[] words, String[] queries) {
int[] answer = {};
//주어진 단어를 트라이 형태로 정렬
//단어 개수별로 트라이 정렬 (정방향, 역방향)
for(String word : words){
int size = word.length();
if(tries[size] == null){
tries[size] = new Trie_SearchLyrics();
}
tries[size].insert(word);
//역방향으로도 트라이 정렬
StringBuilder sb = new StringBuilder(word);
String wordReversed = sb.reverse().toString();
if(tries_reversed[size] == null) tries_reversed[size] = new Trie_SearchLyrics();
tries_reversed[size].insert(wordReversed);
}
//질문에 대한 처리과정
ArrayList<Integer> answers = new ArrayList<>();
for(String query : queries){
int size = query.length();
//만약에 뒤에 ?가 있다면 -> 해당 단어 개수에 맞는 트라이로 가서 검색
if(query.charAt(query.length()-1) == '?'){
if(tries[size] == null) answers.add(0);
else {
int tmp = tries[size].search(query);
answers.add(tmp);
}
}
//앞에 ?가 있다면 -> 해당 단어 개수에 맞는 트라이로 가서 검색
else {
StringBuilder sb = new StringBuilder(query);
String queryReversed = sb.reverse().toString();
if(tries_reversed[size] == null) answers.add(0);
else {
int tmp = tries_reversed[size].search(queryReversed);
answers.add(tmp);
}
}
}
answer = new int[answers.size()];
for(int i=0; i<answer.length; ++i){
answer[i] = answers.get(i);
}
return answer;
}
}
트라이를 글자 개수에 따라 배열로 관리한다는 방법과, 접두사 접미사의 특성을 고려해 문자 역순으로도 관리하는 방법에 대해 깊은 인상을 받았다. 꼭 하나의 트라이로만 관리할 필요가 없었는데... 분류시키는 방법과 아이디어를 계속해서 습관화시켜야겠다.
'Algorithm > Problem_프로그래머스' 카테고리의 다른 글
알고리즘(도둑질) (0) | 2019.12.02 |
---|---|
알고리즘(징검 다리, 카드 게임) (0) | 2019.11.27 |
알고리즘(베스트 앨범, 보행자 천국) (0) | 2019.11.22 |
알고리즘(등굣길, 순위) (0) | 2019.11.21 |
프로그래머스(여행 경로, 저울) - Java (0) | 2019.11.18 |