
내가 생각했을때 문제에서 원하는부분
입력의 첫째 줄에 석규가 기억하는 원본 알파벳의 수 N(1 ≤ N ≤ 100)과 포스트잇의 개수 M(1 ≤ M ≤ 1000)이 주어진다.
다음 줄에 길이가 N인 알파벳 대문자로 이루어진 문자열 S가 주어진다.
이 후 M개의 줄에 알파벳 대문자로 이루어진 판별해야 할 포스트잇들이 주어진다.
모든 포스트잇에 적힌 문자열은 1000자 이하이다.
M개의 줄에 가능성 여부를 “true” , “false”로 답하여라.
내가 이 문제를 보고 생각해본 부분
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));: 표준 입력(System.in)으로부터 데이터를 읽어오기 위한 BufferedReader 객체를 생성한다.
InputStreamReader는 바이트 스트림(System.in)을 문자 스트림으로 변환해주는 역할을 한다.
BufferedReader는 이 문자 스트림에서 데이터를 버퍼링하여 한 줄씩 효율적으로 읽을 수 있게 도와준다.
StringTokenizer st = new StringTokenizer(br.readLine());: br.readLine()을 호출하여 입력의 첫 번째 줄(예: "4 5")을 읽어온다.
이렇게 읽어온 한 줄의 문자열을 StringTokenizer 객체의 인자로 넘겨서, 해당 문자열을 공백(기본 구분자)을 기준으로 나눌 수 있도록 준비한다.
int N = Integer.parseInt(st.nextToken());: StringTokenizer 객체 st에서 다음 토큰(여기서는 "4")을 가져온다.
st.nextToken()은 문자열 타입이므로, Integer.parseInt() 메서드를 사용하여 정수형 int 타입으로 변환한 후 N 변수에 저장한다.
N은 석규가 기억하는 비밀번호 문자열 S의 길이를 나타낸다.
int M = Integer.parseInt(st.nextToken());: 마찬가지로 st에서 그 다음 토큰(여기서는 "5")을 가져와 int 타입으로 변환한 후 M 변수에 저장한다.
M은 석규가 확인해야 할 포스트잇의 총 개수를 나타낸다.
String S = br.readLine();: BufferedReader 객체 br을 사용하여 입력의 두 번째 줄(예: "PPAP")을 읽어온다.
이 문자열은 석규가 정확히 기억하는 N글자의 비밀번호를 의미하며, S 변수에 저장된다.
for(int i = 0; i < M; i++) { ... }: M은 포스트잇의 총 개수이므로, 이 for 루프는 M번 반복하면서 각 포스트잇에 대한 처리를 수행한다.
String postIt = br.readLine();: 루프가 한 번 실행될 때마다 br.readLine()을 통해 새로운 포스트잇 문자열을 한 줄씩 읽어와 postIt 변수에 저장한다.
if(isSubsequence(N, S, postIt)) { ... } else { ... }: 읽어온 postIt 문자열이 S의 부분 수열인지 확인하기 위해 isSubsequence라는 사용자 정의 함수를 호출한다.
이 함수는 true 또는 false를 반환한다.
isSubsequence(N, S, postIt): 석규가 기억하는 비밀번호의 길이 N, 비밀번호 문자열 S, 그리고 현재 확인 중인 포스트잇 문자열 postIt를 인자로 전달한다.
함수 결과가 true이면 "true"를 출력하고, false이면 "false"를 출력한다.
br.close();: 모든 입력 처리가 끝나면, 사용했던 BufferedReader 객체 br을 닫는다.
public static boolean isSubsequence(int sLength, String s, String t): 이 함수는 두 개의 문자열 s (석규가 기억하는 비밀번호)와 t (포스트잇 문자열)를 받아서 s가 t의 부분 수열인지 여부를 boolean 값으로 반환한다.
sLength는 s의 길이이며 N 값을 받는다.
int sPointer = 0;: 문자열 s의 현재 인덱스를 추적하는 포인터입니다. 초기값은 0으로, s의 첫 번째 문자부터 시작한다.
int tPointer = 0;: 문자열 t의 현재 인덱스를 추적하는 포인터입니다. 초기값은 0으로, t의 첫 번째 문자부터 시작한다.
while(sPointer < sLength && tPointer < t.length()) { ... }: 이 while 루프는 s의 모든 문자를 찾지 못했거나 (sPointer < sLength) 또는 t의 끝에 아직 도달하지 않았다면 (tPointer < t.length()) 계속 반복된다.
둘 중 하나의 조건이라도 false가 되면 루프가 종료된다.
if(s.charAt(sPointer) == t.charAt(tPointer)) { sPointer++; }: s의 sPointer 위치에 있는 문자와 t의 tPointer 위치에 있는 문자를 비교한다.
만약 두 문자가 같다면, 이는 s의 현재 문자를 t에서 찾았다는 의미입니다. 따라서 sPointer를 1 증가시켜 s의 다음 문자를 찾을 준비를 한다.
tPointer++;: if 조건이 참이든 거짓이든 상관없이 tPointer는 항상 1 증가합니다. 이는 t 문자열의 모든 문자를 순차적으로 탐색해야 하기 때문이다.
s의 현재 문자를 찾았든 찾지 못했든 t는 다음 문자로 이동하여 탐색을 계속해야 한다.
return sPointer == sLength;: while 루프가 종료된 후, sPointer가 sLength와 같다는 것은 s의 모든 문자를 t에서 순서대로 성공적으로 찾았다는 의미이다.
이 경우 true를 반환하고, 그렇지 않으면 (s의 모든 문자를 찾지 못하고 t의 끝에 도달했다면) false`를 반환한다.
코드로 구현
package baekjoon.baekjoon_31;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
// 백준 15786번 문제
public class Main1211 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
// 첫 줄에서 N과 M을 읽습니다.
int N = Integer.parseInt(st.nextToken()); // 석규가 기억하는 원본 알파벳의 수 (S의 길이)
int M = Integer.parseInt(st.nextToken()); // 포스트잇의 개수
// 다음 줄에서 석규가 기억하는 비밀번호 문자열 S를 읽습니다.
String S = br.readLine();
// 기존 코드에서는 S.length()를 사용했는데, S.length()는 N과 같은 값을 가집니다.
// M개의 포스트잇에 대해 가능성 여부를 판단합니다.
for(int i = 0; i < M; i++) {
String postIt = br.readLine(); // 각 포스트잇 문자열을 읽습니다.
// isSubsequence 함수를 호출하여 부분 수열인지 확인하고 결과를 출력합니다.
// N 변수를 isSubsequence 함수에 전달합니다.
if(isSubsequence(N, S, postIt)) { // N을 첫 번째 인자로 전달
System.out.println("true");
} else {
System.out.println("false");
}
}
br.close(); // BufferedReader를 닫습니다.
}
/**
* s가 t의 부분 수열인지 여부를 판단하는 함수.
* @param sLength 기억하는 비밀번호 문자열 s의 길이 (N 값)
* @param s 기억하는 비밀번호 문자열 (짧은 문자열)
* @param t 포스트잇에 적힌 문자열 (긴 문자열)
* @return s가 t의 부분 수열이면 true, 아니면 false
*/
public static boolean isSubsequence(int sLength, String s, String t) { // sLength 인자 추가
int sPointer = 0; // 문자열 s의 현재 문자를 가리키는 포인터
int tPointer = 0; // 문자열 t의 현재 문자를 가리키는 포인터
// t의 끝에 도달하거나 s의 모든 문자를 찾을 때까지 반복합니다.
while(sPointer < sLength && tPointer < t.length()) { // s.length() 대신 sLength 사용
// s의 현재 문자와 t의 현재 문자가 일치하면 sPointer를 증가시킵니다.
// 이는 s의 다음 문자를 t에서 찾아야 한다는 것을 의미합니다.
if(s.charAt(sPointer) == t.charAt(tPointer)) {
sPointer++;
}
// tPointer는 항상 증가시켜 t의 다음 문자로 이동합니다.
// s의 문자를 찾든 못 찾든 t를 계속 탐색해야 합니다.
tPointer++;
}
// 반복문이 끝난 후 sPointer가 sLength와 같아졌다면,
// 이는 s의 모든 문자를 t에서 순서대로 찾았다는 의미입니다.
return sPointer == sLength; // s.length() 대신 sLength 사용
}
}
코드와 설명이 부족할수 있습니다. 코드를 보시고 문제가 있거나 코드 개선이 필요한 부분이 있다면 댓글로 말해주시면 감사한 마음으로 참고해 코드를 수정 하겠습니다.