Post

BOJ_10840_구간 성분 (Java)

BOJ_10840_구간 성분 (Java)

[Gold I] 구간 성분 - 10840

문제 링크

성능 요약

메모리: 226464 KB, 시간: 936 ms

분류

해싱

제출 일자

2024년 8월 4일 21:34:43

문제 설명

매 초마다 신호를 발생시키는 두 장치 A, B가 있다. 이 신호는 알파벳 소문자의 서열로 표현된다. A, B로부터 발생한 신호를 서열로 표시한 SA, SB의 예는 다음과 같다.

  • SA = [a, f, c, d, r, d, e, s, d, c, f, w, s, z, r]
  • SB = [g, e, d, s, r, d, d, e, m, z, r]

신호 서열의 어떤 구간에 포함된 문자의 종류와 개수가 순서에 상관없이 동일하면 이 두 ‘구간의 성분’은 같다고 한다. 아래에서 박스로 표시된 부분은 두 신호 SA, SB에서 성분이 같은 구간을 나타내고 있다.

즉 위의 예와 같이 성분이 같은 구간의 길이는 두 서열에서 반드시 같아야 한다. 그리고 같은 성분 의 구간은 하나 이상 존재할 수 있다. 우리는 두 신호 서열에 각각 존재하는 같은 성분 구간 중에 서 가장 긴 것을 찾으려고 한다.

입력

첫 두 줄에 신호 서열이 공백 없는 하나의 문자열로 각각 주어진다. 이 문자열은 영문 소문자로만 구성되어 있다. 두 입력 문자열의 크기 N, M의 범위는 1 ≤ N, M ≤ 1,500 이다.

출력

두 서열에서 같은 성분을 가진 구간 중에서 가장 긴 구간을 찾아, 그 구간의 길이를 출력해야 한다.

문제 풀이

문제 풀이문제 풀이

코드

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import java.io.*;
import java.util.*;

public class BOJ_10840_구간성분 {
	static BufferedReader br;
	static BufferedWriter bw;
	static String A, B;
	static boolean isExist = false; // A랑 B에 같은게 있는지 없는지 찾기 위한 변
	static long[] hashTable = new long[26]; // a~z를 31의 거듭제곱으로 표현
	public static void main(String[] args) throws IOException {
//		br = new BufferedReader(new InputStreamReader(System.in));
		br = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
		bw = new  BufferedWriter(new OutputStreamWriter(System.out));
		
		String s1 = br.readLine(); String s2 = br.readLine();
		if(s1.length() > s2.length()) {
			String tmp = s1;
			s1 = s2;
			s2 = tmp;
		}
		A = s1; B = s2; // A 가 B보다 항상 짧거나 같도록만듬.
		
		for(int i=0; i<26; i++) {
			if(i==0) hashTable[0] = 1;
			else hashTable[i] = 31 * hashTable[i-1];
		}
//		System.out.println(Arrays.toString(hashTable));
		
		int maxLen = 0;
		for(int i=1; i<=A.length(); i++) { // 작은문자열 기준 길이 1부터 그길이 자체까지 비교해봐야함 
			Set<Long> set_A = new HashSet<Long>();
			Set<Long> set_B = new HashSet<Long>();
			
			long hashVal_A = 0; long hashVal_B = 0;
			for(int j = 0; j<= B.length() - i; j++) {
				/**
				 * i 길이에 해당하는 만큼 hashVal 구하기
				 * 처음에 hash값 구하고 한칸씩 왼쪽 -1 오른쪽 +1 시키면서 중복부분 기억하여 계산 최적화 
				 * 
				 */
				if(j== 0) {
					hashVal_A = hash(A, i); 
					hashVal_B = hash(B, i);	
				} else {
					/**
					 * i길이만큼 자른것이 끝에 도달했는지 체크( unit의 끝 인덱스 j +i - 1, 원래 A의 인덱스 A.length() - 1
					 * A
					 * 빼줄 left = A.charAt(j-1) - 'a'
					 * 더해줄 right = A.charAt(j + (i - 1)) - 'a'
					 * 
					 * B
					 * 빼줄 left = B.charAt(j-1) - 'a'
					 * 더해줄 right = B.charAt(j + (i - 1)) - 'a' 
					 */
					if(j+(i-1) <= A.length() -1) { // 경계조건 check
						hashVal_A = hashVal_A - hashTable[A.charAt(j-1) - 'a'] + hashTable[A.charAt(j + (i - 1))-'a'];
					}
					
					hashVal_B =hashVal_B - hashTable[B.charAt(j-1) - 'a'] + hashTable[B.charAt(j + (i - 1))-'a'];
				}
				// 나온 hash값을 각 set_A와 set_B에 추가
				set_A.add(hashVal_A);
				set_B.add(hashVal_B);
			
			} // 길이별 해시값 추가 종료 
			
			// 이제 set_A길이 + set_B 길이가 A와 B 중복체크한 의 set과 원소 개수 같은지 check
			Set<Long> newSet = new HashSet<Long>();
			newSet.addAll(set_A);
			newSet.addAll(set_B);
			
			isExist = set_A.size() + set_B.size() == newSet.size() ? false : true;
			if(isExist) maxLen = i;
			
		} //  특정 길이별 계산 종료 
		
		bw.write(String.valueOf(maxLen));
		bw.flush();
		bw.close();
		br.close();

	}
	
	/**
	 * 해시값 만들기 - 카운팅 배열로 알파벳 개수 세고 그거 토대로 hash값을 hashTable값 곱해서 만들기
	 * 
	 * @param s
	 * @param l
	 * @return
	 */
	private static long hash(String s, int l) {
		long hash = 0;
		int[] count = new int[26];
		for(int i=0; i<l; i++) {
			count[s.charAt(i)- 'a']++;
		}
		for(int i=0; i< 26; i++) {
			hash += count[i] * hashTable[i];
		}
		return hash;
	}
}

This post is licensed under CC BY 4.0 by the author.