코딩테스트/백준

[Java] 백준 22861번 - 폴더 정리 (large)

곰돌이볼 2023. 4. 26. 09:19

 

  • 조건
    • 같은 폴더 존재 X
    • 같은 폴더 내에서 같은 파일명 존재 X
    • 같은 이름의 파일은 서로 같은 내용을 가지고 있음
    • 모든 폴더는 하나 이상의 파일을 가지고 있음

 

  • 입력
    • 첫번째 줄
      • N : main 폴더 안에 있는 폴더의 총 개수 (1 ≤ N ≤1,000)
      • M : 파일의 총 개수 (1 ≤ M ≤ 1,000)
    • 두번째 줄 ~ (N+M+1)번째 줄
      • P : 상위 폴더의 이름 (1 ≤ |P| ≤ 10)
      • F : 폴더 또는 파일의 이름 (1 ≤ |F| ≤ 10)
      • C : 폴더 = 1, 파일 = 0
    • (N+M+2)번째 줄
      • K : 옮기는 횟수 (0 ≤ K ≤ 1,000)
    • (N+M+3)번째 줄 ~ (N+M+K+3)번째 줄
      • 이동 전 폴더 경로 A 
      • 이동할 폴더 경로 B
      • 경로 A의 하위 파일을 경로 B의 하위 파일로 이동
    • (N+M+K+4)번째 줄
      • Q : 쿼리의 개수 (1 ≤ Q ≤ 1,000)
    • (N+M+K+5)번째 줄 ~ (N+M+K+Q+5)번째 줄
      • 파일의 쿼리

 

  • 출력
    • 쿼리 순서대로 폴더 하위에 있는 파일 종류의 개수와 파일의 총 개수
      • 파일 종류의 개수 : 같은 이름의 파일이 있는 경우 → 같은 종류로 포함
      • 파일의 총 개수 : 같은 이름의 파일이 있는 경우 → 하나로 계산하지 않음

 

  • 찾아낸 규칙
    •  

 

  • 로직 1)
import java.lang.*;
import java.text.NumberFormat;
import java.util.*;
import java.util.stream.Collectors;

public class Main {
    // 폴더를 입력받으면, 폴더의 쿼리 반환
    static String findKey(String key, Map<String, Set<String>> map) {
        // key가 포함된 쿼리가 있는 것만 필터링
        List<String> keys = map.keySet().stream().filter(s -> s.contains(key)).collect(Collectors.toList());

        // 상위 폴더가 없는 경우
        if(keys.isEmpty()) return "";

        // 상위 폴더가 있는 경우
        String[] keyArray = keys.get(0).split("/");
        String query = keyArray[0];
        if(!keyArray[0].equals(key)) {
            for(int i=1; i<keyArray.length; i++) {
                query = query + "/" +keyArray[i];
                if(keyArray[i].equals(key)) break;
            }
        }

        return query;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Map<String, Set<String>> map = new HashMap<>();
        Queue<String> queueFolder = new LinkedList<>();
        Queue<String> queuefile = new LinkedList<>();

        // 폴더와 파일의 개수 합
        int count = sc.nextInt() + sc.nextInt();
        for(int i=0; i<count; i++) {
            String P = sc.next(); // 상위 폴더 이름
            String F = sc.next(); // 폴더 또는 파일 이름
            int C = sc.nextInt(); // 1 = 폴더, 0 = 파일

            String highFolder = findKey(P, map); // 상위 폴더의 쿼리 찾기
            // 폴더인 경우
            if(C == 1) {
                if(P.equals("main")) { // 상위 폴더가 없는 경우
                    map.put(P+"/"+F, new HashSet<>());
                } else { // 상위 폴더가 있는 경우
                    if(map.get(highFolder) == null) {
                        queueFolder.add(P);
                        queueFolder.add(F);
                    } else map.put(highFolder+"/"+F, new HashSet<>());
                }
            } else { // 파일인 경우
                if(map.get(highFolder) == null) {
                    queuefile.add(P);
                    queuefile.add(F);
                } else map.get(highFolder).add(F);
            }
        }

       // 폴더인 경우
        while(!queueFolder.isEmpty()) {
            String P = queueFolder.poll();
            String F = queueFolder.poll();
            String highFolder = findKey(P, map);

            if(map.get(highFolder) == null) {
                queueFolder.add(P);
                queueFolder.add(F);
            } else map.put(highFolder+"/"+F, new HashSet<>());
        }

        // 파일인 경우
        while(!queuefile.isEmpty()) {
            String P = queuefile.poll();
            String F = queuefile.poll();
            String highFolder = findKey(P, map);

            if(map.get(highFolder) == null) {
                queuefile.add(P);
                queuefile.add(F);
            } else map.get(highFolder).add(F);
        }

        // 파일 이동
        count = sc.nextInt();
        for(int i=0; i<count; i++) {
            // 폴더A와 폴더B의 폴더 경로
            String A = sc.next();
            String B = sc.next();

            // 폴더A, 폴더B의 value 찾기
            Set<String> folderA = map.get(A);
            Set<String> folderB = map.get(B);

            // 폴더A의 파일을 폴더B로 이동 및 폴더A 삭제
            if(folderA != null) {
                throw new NumberFormatException();
            }
            folderB.addAll(folderA);
            map.remove(A, folderA);

            // 폴더A 내에 있는 폴더의 경로 변경
            List<String> lowFolders = map.keySet().stream().filter(s -> s.contains(A)).collect(Collectors.toList()); // 폴더A 안에 있는 폴더명 검색
            for(int j=0; j<lowFolders.size(); j++) {
                map.put(lowFolders.get(j).replace(A, B), map.get(lowFolders.get(j)));
                map.remove(lowFolders.get(j));
            }
        }

        // 폴더 내 파일 종류와 개수 출력하기
        count = sc.nextInt();
        for(int i=0; i<count; i++) {
            Set<String> fileType = new HashSet<>(); // 파일의 종류
            List<String> fileCount = new ArrayList<>(); // 파일의 개수

            String findFolder = sc.next(); // 탐색할 폴더
            List<String> findFolders = map.keySet().stream().filter(s -> s.contains(findFolder)).collect(Collectors.toList()); // 탐색할 폴더 안에 있는 폴더명 검색
            for(int j=0; j<findFolders.size(); j++) {
                Set<String> values = map.get(findFolders.get(j));
                // 탐색할 폴더 내부에 있는 파일 추가하기
                if(values != null) fileType.addAll(values);
                if(values != null) fileCount.addAll(values);
            }

            System.out.println(fileType.size() + " " + fileCount.size());
        }
    }
}