<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>stquokka</title>
    <link>https://stquokka.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 11 Jun 2026 18:24:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>quokkaST</managingEditor>
    <image>
      <title>stquokka</title>
      <url>https://tistory1.daumcdn.net/tistory/5043897/attach/0cdf0fea3276446590a859e1da82af81</url>
      <link>https://stquokka.tistory.com</link>
    </image>
    <item>
      <title>VS Code 원격 개발 환경 구성: SSH 키로 원격 서버 접속하기</title>
      <link>https://stquokka.tistory.com/91</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 VS Code의 &lt;b&gt;Remote - SSH&lt;/b&gt; 확장 기능을 사용하여,&lt;br /&gt;비밀번호 대신 &lt;b&gt;SSH 키&lt;/b&gt;를 통해 원격 컴퓨터에 안전하고 편리하게 접속하는 방법을 단계별로 정리하였다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 왜 비밀번호 대신 SSH 키를 사용해야 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 비밀번호 입력 방식은 간단하지만 보안에 취약하다.&lt;br /&gt;SSH 키 인증은 &lt;b&gt;비대칭 키 암호화&lt;/b&gt;를 기반으로 하여 보안성과 편의성을 동시에 제공한다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;비밀번호 인증&lt;/th&gt;
&lt;th&gt;SSH 키 인증&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보안 수준&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;낮음 (유출, 무차별 대입 공격에 취약)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;높음&lt;/b&gt; (개인 키 없이는 접속 불가)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;편의성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;매번 입력 필요&lt;/td&gt;
&lt;td&gt;최초 1회 등록 후 비밀번호 없이 접속&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;활용성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;수동 접속에 적합&lt;/td&gt;
&lt;td&gt;자동화, 반복 작업에 유리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. SSH 키 생성 (로컬 PC)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널(Windows는 PowerShell 또는 Git Bash)에서 다음을 실행한다:&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;-t rsa&lt;/code&gt; : RSA 알고리즘 사용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-b 4096&lt;/code&gt; : 4096비트 키 생성 &amp;rarr; 보안 강화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-C&lt;/code&gt; : 키 설명(식별용, 보통 이메일)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성이 완료되면 &lt;code&gt;~/.ssh/&lt;/code&gt; 폴더에&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;id_rsa&lt;/code&gt; (개인 키, 절대 외부 유출 금지)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id_rsa.pub&lt;/code&gt; (공개 키)&lt;br /&gt;파일이 생성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 공개 키를 원격 서버에 등록&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ssh-copy-id&lt;/code&gt; 명령어를 사용하면 편리하다:&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;ssh-copy-id [사용자명]@[원격서버_IP]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마지막으로 비밀번호를 1회 입력&lt;/li&gt;
&lt;li&gt;공개 키가 원격 서버 &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;에 자동 추가됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;code&gt;ssh-copy-id&lt;/code&gt;를 사용할 수 없다면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬에서 &lt;code&gt;cat ~/.ssh/id_rsa.pub&lt;/code&gt; 실행 &amp;rarr; 공개 키 내용 복사&lt;/li&gt;
&lt;li&gt;원격 서버 &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; 파일에 수동으로 붙여넣기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. VS Code에서 원격 접속&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;VS Code 실행 후 좌측 메뉴에서 &lt;b&gt;Remote Explorer&lt;/b&gt; 클릭&lt;/li&gt;
&lt;li&gt;상단 메뉴에서 &lt;b&gt;SSH&lt;/b&gt; 선택&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ssh [사용자명]@[원격서버_IP]&lt;/code&gt; 입력 &amp;rarr; Enter&lt;/li&gt;
&lt;li&gt;SSH 설정 파일(config) 저장 (기본값 추천)&lt;/li&gt;
&lt;li&gt;SSH Targets 목록에 서버가 나타나면 연결 아이콘 클릭&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 창이 열리면서 원격 서버와 연결됨.&lt;br /&gt;&amp;rarr; 원격 파일 편집, 터미널 사용 가능.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;학습 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSH 키 기반 접속은 단순한 설정 이상의 가치가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;보안성 강화&lt;/b&gt; : 비밀번호 노출 위험 감소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;작업 효율성 향상&lt;/b&gt; : 반복 로그인 불필요, 자동화 작업 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발 생산성 증대&lt;/b&gt; : VS Code 원격 기능을 통해 로컬 제약 없이 개발 환경을 확장&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS공부/인프라</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/91</guid>
      <comments>https://stquokka.tistory.com/91#entry91comment</comments>
      <pubDate>Mon, 1 Sep 2025 14:25:34 +0900</pubDate>
    </item>
    <item>
      <title>OAuth2 기반 Google &amp;amp; Kakao 로그인 구축 방법</title>
      <link>https://stquokka.tistory.com/90</link>
      <description>&lt;h2&gt;✅ 사전 준비&lt;/h2&gt;
&lt;h3&gt;  공통&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Spring Boot (spring-boot-starter-oauth2-client)&lt;/li&gt;
&lt;li&gt;Redirect URI: &lt;code&gt;https://yourdomain.com/login/oauth2/code/{provider}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  1. Google OAuth2 로그인 구축&lt;/h2&gt;
&lt;h3&gt;  Google Cloud 설정&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://console.cloud.google.com/&quot;&gt;Google Cloud Console&lt;/a&gt; 접속&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OAuth 동의 화면&lt;/strong&gt; 구성 (앱 이름, 사용자 이메일 등)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OAuth 2.0 클라이언트 ID&lt;/strong&gt; 생성&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;애플리케이션 유형: 웹 애플리케이션&lt;/li&gt;
&lt;li&gt;승인된 리디렉션 URI:&lt;br&gt;&lt;code&gt;https://yourdomain.com/login/oauth2/code/google&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;클라이언트 ID, 클라이언트 시크릿 발급&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;⚙️ application.yml 설정&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: YOUR_GOOGLE_CLIENT_ID
            client-secret: YOUR_GOOGLE_CLIENT_SECRET
            scope: profile, email
        provider:
          google:
            authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
            token-uri: https://oauth2.googleapis.com/token
            user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo
            user-name-attribute: sub&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  2. Kakao OAuth2 로그인 구축&lt;/h2&gt;
&lt;h3&gt;  Kakao 개발자 설정&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://developers.kakao.com/&quot;&gt;Kakao Developers&lt;/a&gt; 접속&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;애플리케이션 생성&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;플랫폼 &amp;gt; 웹 사이트 도메인 등록: &lt;code&gt;https://yourdomain.com&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Redirect URI 등록:&lt;br&gt;&lt;code&gt;https://yourdomain.com/login/oauth2/code/kakao&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;REST API 키 획득&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;⚙️ application.yml 설정&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;spring:
  security:
    oauth2:
      client:
        registration:
          kakao:
            client-id: YOUR_KAKAO_REST_API_KEY
            redirect-uri: &amp;quot;{baseUrl}/login/oauth2/code/{registrationId}&amp;quot;
            client-authentication-method: POST
            authorization-grant-type: authorization_code
            scope:
              - profile_nickname
              - account_email
            client-name: Kakao
        provider:
          kakao:
            authorization-uri: https://kauth.kakao.com/oauth/authorize
            token-uri: https://kauth.kakao.com/oauth/token
            user-info-uri: https://kapi.kakao.com/v2/user/me
            user-name-attribute: id&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  커스터마이징&lt;/h2&gt;
&lt;h3&gt;✅ 사용자 정보 매핑 (OAuth2UserService)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        String registrationId = userRequest.getClientRegistration().getRegistrationId();

        Map&amp;lt;String, Object&amp;gt; attributes = oAuth2User.getAttributes();
        if (registrationId.equals(&amp;quot;kakao&amp;quot;)) {
            attributes = (Map&amp;lt;String, Object&amp;gt;) attributes.get(&amp;quot;kakao_account&amp;quot;);
        }

        return new DefaultOAuth2User(
            Collections.singleton(new SimpleGrantedAuthority(&amp;quot;ROLE_USER&amp;quot;)),
            attributes,
            &amp;quot;email&amp;quot;
        );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;✅ Security 설정&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests()
                .requestMatchers(&amp;quot;/&amp;quot;, &amp;quot;/login/**&amp;quot;, &amp;quot;/oauth2/**&amp;quot;).permitAll()
                .anyRequest().authenticated()
            .and()
                .oauth2Login()
                .userInfoEndpoint()
                    .userService(customOAuth2UserService);
        return http.build();
    }

    @Autowired
    private CustomOAuth2UserService customOAuth2UserService;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  실무 팁&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Redirect URI는 반드시 &lt;code&gt;https&lt;/code&gt; 기반이어야 함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HttpOnly&lt;/code&gt; + &lt;code&gt;Secure&lt;/code&gt; 쿠키 사용 고려&lt;/li&gt;
&lt;li&gt;JWT 발급 연동 시 OAuth 로그인 후 &lt;code&gt;accessToken&lt;/code&gt; 생성하도록 구현 가능&lt;/li&gt;
&lt;li&gt;회원가입 연동 시 이메일 중복 처리 / 닉네임 생성 로직 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description>
      <category>CS공부/Java &amp;amp; Spring</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/90</guid>
      <comments>https://stquokka.tistory.com/90#entry90comment</comments>
      <pubDate>Mon, 30 Jun 2025 23:47:09 +0900</pubDate>
    </item>
    <item>
      <title>백준 2615: 소형기관차(Java)</title>
      <link>https://stquokka.tistory.com/89</link>
      <description>&lt;h1&gt;  백준 2616번: 소형기관차&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  문제 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N개의 객차(1 &amp;le; N &amp;le; 50,000)가 수직선 상에 있다.&lt;/li&gt;
&lt;li&gt;각 객차에는 손님이 타고 있다 (최대 100명).&lt;/li&gt;
&lt;li&gt;소형 기관차는 최대 &lt;code&gt;K&lt;/code&gt;칸까지 끌 수 있으며, 총 3대가 존재한다.&lt;/li&gt;
&lt;li&gt;3개의 소형 기관차가 &lt;b&gt;서로 겹치지 않게&lt;/b&gt; 총합 손님 수가 최대가 되도록 운행할 때의 &lt;b&gt;최대 손님 수&lt;/b&gt;를 구하라.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  문제 접근&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 핵심 개념&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3개의 기관차가 끌 수 있는 객차를 비중복으로 선택 &amp;rarr; 구간 합 최적화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;누적합&lt;/code&gt; + &lt;code&gt;DP&lt;/code&gt; 사용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dp[i][j]&lt;/code&gt;: i번째 기관차까지 사용해서, j번째 객차까지 탐색했을 때 최댓값&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  알고리즘 설계&lt;/h2&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());

        int[] people = new int[N + 1];
        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 1; i &amp;lt;= N; i++) {
            people[i] = Integer.parseInt(st.nextToken());
        }

        int K = Integer.parseInt(br.readLine());

        int[] total = new int[N + 1];
        for (int i = 1; i &amp;lt;= N; i++) {
            total[i] = total[i - 1] + people[i];
        }

        int[][] dp = new int[4][N + 1];

        for (int i = 1; i &amp;lt;= 3; i++) {
            for (int j = K * i; j &amp;lt;= N; j++) {
                int curtotal = total[j] - total[j - K];
                dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j - K] + curtotal);
            }
        }

        System.out.println(dp[3][N]);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 시간 복잡도&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;누적합 계산: O(N)&lt;/li&gt;
&lt;li&gt;DP 테이블 채우기: O(N)&lt;/li&gt;
&lt;li&gt;전체 시간복잡도: O(N) &amp;rarr; N &amp;le; 50,000 이므로 충분히 통과 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;핵심은 &lt;b&gt;비중복 구간 합&lt;/b&gt; 최적화 문제를 &lt;b&gt;DP + 누적합&lt;/b&gt;으로 해결하는 것&lt;/li&gt;
&lt;li&gt;상태 전이: &lt;code&gt;dp[i][j] = max(dp[i][j - 1], dp[i - 1][j - K] + sum(j-K+1 ~ j))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;알고리즘 구현 난이도는 낮지만, 상태 정의를 이해하는 것이 중요하다&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>알고리즘</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/89</guid>
      <comments>https://stquokka.tistory.com/89#entry89comment</comments>
      <pubDate>Sun, 29 Jun 2025 21:23:56 +0900</pubDate>
    </item>
    <item>
      <title>백준 2306: 유전자(Java)</title>
      <link>https://stquokka.tistory.com/88</link>
      <description>&lt;h2&gt;  목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;주어진 DNA 문자열에서 KOI 유전자 조건을 만족하는 &lt;strong&gt;최장 유효 부분 문자열의 길이&lt;/strong&gt;를 구한다.&lt;/li&gt;
&lt;li&gt;KOI 유전자 정의:&lt;ul&gt;
&lt;li&gt;&lt;code&gt;at&lt;/code&gt;, &lt;code&gt;gc&lt;/code&gt;는 KOI 유전자.&lt;/li&gt;
&lt;li&gt;KOI 유전자 X → &lt;code&gt;aXt&lt;/code&gt;, &lt;code&gt;gXc&lt;/code&gt;도 KOI 유전자.&lt;/li&gt;
&lt;li&gt;X, Y가 KOI 유전자 → XY도 KOI 유전자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 핵심 아이디어&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DP[i][j]&lt;/strong&gt;: i~j까지 KOI 유전자 중 최장 길이&lt;/li&gt;
&lt;li&gt;두 가지 전이 방식:&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;경계 조건&lt;/strong&gt;: (i,j) 가 &lt;code&gt;a,t&lt;/code&gt; or &lt;code&gt;g,c&lt;/code&gt;로 감싸진다면 → 내부 + 2&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;분할 결합&lt;/strong&gt;: &lt;code&gt;dp[i][j] = max(dp[i][k] + dp[k+1][j])&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  알고리즘 설계&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import java.io.*;
import java.util.*;

public class Main {
    static int[][] dp;
    static char[] dna;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        dna = br.readLine().toCharArray();
        int n = dna.length;

        dp = new int[n][n];

        for (int len = 2; len &amp;lt;= n; len++) {
            for (int i = 0; i + len - 1 &amp;lt; n; i++) {
                int j = i + len - 1;

                if (isPair(dna[i], dna[j])) {
                    dp[i][j] = Math.max(dp[i][j], 2 + (i + 1 &amp;lt;= j - 1 ? dp[i + 1][j - 1] : 0));
                }

                for (int k = i; k &amp;lt; j; k++) {
                    dp[i][j] = Math.max(dp[i][j], dp[i][k] + dp[k + 1][j]);
                }
            }
        }

        System.out.println(dp[0][n - 1]);
    }

    static boolean isPair(char a, char b) {
        return (a == &amp;#39;a&amp;#39; &amp;amp;&amp;amp; b == &amp;#39;t&amp;#39;) || (a == &amp;#39;g&amp;#39; &amp;amp;&amp;amp; b == &amp;#39;c&amp;#39;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;⏱ 시간복잡도&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;O(N³): i, j, k 세중 루프&lt;/li&gt;
&lt;li&gt;N ≤ 500이므로 Java에서도 통과 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  정리&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;이 문제는 전형적인 &lt;strong&gt;문자열 DP&lt;/strong&gt; 문제이며, &lt;strong&gt;분할 정복적 사고&lt;/strong&gt;와 &lt;strong&gt;경계 조건 처리&lt;/strong&gt;가 핵심&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dp[i][j] = max(경계 포함, 분할)&lt;/code&gt; 형태를 잘 이해하자&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;</description>
      <category>알고리즘</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/88</guid>
      <comments>https://stquokka.tistory.com/88#entry88comment</comments>
      <pubDate>Thu, 12 Jun 2025 23:29:36 +0900</pubDate>
    </item>
    <item>
      <title>백준 1557: 도로의 개수(Java)</title>
      <link>https://stquokka.tistory.com/87</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 문제 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;N&amp;times;M&lt;/code&gt; 격자 도시에 도로가 건설됨.&lt;/li&gt;
&lt;li&gt;도로는 격자의 가로선 또는 세로선에 해당.&lt;/li&gt;
&lt;li&gt;일부 도로는 공사 중이므로 통행 불가.&lt;/li&gt;
&lt;li&gt;(0,0)에서 (N,M)까지 이동하는 &lt;b&gt;경로의 수&lt;/b&gt;를 구하라.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오른쪽&lt;/b&gt; 또는 &lt;b&gt;아래쪽&lt;/b&gt; 방향으로만 이동 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  알고리즘 설계&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DP[i][j] = (0,0)부터 (i,j)까지 가는 경로 수&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;인접한 두 좌표 사이의 도로가 막혀있다면 그 방향으로 이동 불가.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;공사 중 도로&lt;/code&gt;는 (i1, j1, i2, j2)로 주어짐 &amp;rarr; &lt;code&gt;Set&lt;/code&gt;으로 관리.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;import java.util.*;

public class Main {
    static int N, M;
    static long[][] dp;
    static Set&amp;lt;String&amp;gt; block = new HashSet&amp;lt;&amp;gt;();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        M = sc.nextInt();
        N = sc.nextInt();
        int K = sc.nextInt();
        dp = new long[N + 1][M + 1];

        for (int k = 0; k &amp;lt; K; k++) {
            int i1 = sc.nextInt();
            int j1 = sc.nextInt();
            int i2 = sc.nextInt();
            int j2 = sc.nextInt();
            block.add(i1 + &quot; &quot; + j1 + &quot; &quot; + i2 + &quot; &quot; + j2);
            block.add(i2 + &quot; &quot; + j2 + &quot; &quot; + i1 + &quot; &quot; + j1);
        }

        dp[0][0] = 1;
        for (int i = 0; i &amp;lt;= N; i++) {
            for (int j = 0; j &amp;lt;= M; j++) {
                if (i &amp;gt; 0 &amp;amp;&amp;amp; !block.contains((i - 1) + &quot; &quot; + j + &quot; &quot; + i + &quot; &quot; + j)) {
                    dp[i][j] += dp[i - 1][j];
                }
                if (j &amp;gt; 0 &amp;amp;&amp;amp; !block.contains(i + &quot; &quot; + (j - 1) + &quot; &quot; + i + &quot; &quot; + j)) {
                    dp[i][j] += dp[i][j - 1];
                }
            }
        }
        System.out.println(dp[N][M]);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  핵심 포인트&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DP를 활용하여 각 좌표까지의 경로 수를 누적&lt;/li&gt;
&lt;li&gt;공사 중 도로는 &lt;code&gt;Set&amp;lt;String&amp;gt;&lt;/code&gt;으로 관리하여 &lt;b&gt;양방향 차단&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;좌표 인덱스를 (i, j) 형식&lt;/b&gt;으로 명확하게 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⏱ 시간 복잡도 분석&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 복잡도: &lt;code&gt;O(N &amp;times; M)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;공간 복잡도: &lt;code&gt;O(N &amp;times; M)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;N, M &amp;le; 100 이므로 충분히 처리 가능&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>알고리즘</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/87</guid>
      <comments>https://stquokka.tistory.com/87#entry87comment</comments>
      <pubDate>Wed, 11 Jun 2025 22:05:46 +0900</pubDate>
    </item>
    <item>
      <title>GitHub 대용량 파일 관리를 위한 Lens 사용법</title>
      <link>https://stquokka.tistory.com/86</link>
      <description>&lt;h2&gt;  목표&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;대용량 파일로 인한 GitHub 제한 해결&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Git 저장소의 용량 및 속도 문제 해결  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lens CLI를 통한 대체 Git LFS 관리&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;대용량 파일을 별도 스토리지에 분리 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 왜 대용량 파일 관리가 필요한가?&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문제점&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Git 저장소 용량 한계&lt;/td&gt;
&lt;td&gt;GitHub는 기본적으로 파일당 100MB, 전체 1GB 저장소 제한이 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;커밋 내역 무거움&lt;/td&gt;
&lt;td&gt;바이너리/미디어 파일을 자주 커밋하면 기록이 빠르게 커짐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클론/푸시 속도 저하&lt;/td&gt;
&lt;td&gt;저장소 크기가 커지면 협업 속도 저하&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;  이를 해결하기 위해 &lt;code&gt;Git LFS&lt;/code&gt; 또는 대체 도구 &lt;code&gt;Lens&lt;/code&gt;를 사용&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  Git LFS의 한계&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;GitHub에서 공식 지원하지만 무료 저장소는 용량/트래픽 제한이 있음&lt;/li&gt;
&lt;li&gt;1GB 저장, 월 1GB 트래픽 → 초과 시 유료 플랜 필요&lt;/li&gt;
&lt;li&gt;커스텀 파이프라인(CI/CD) 연동 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  Lens란?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Lens&lt;/strong&gt;는 대용량 파일을 Git 히스토리에 포함시키지 않고도 GitHub에서 관리 가능한 &lt;strong&gt;클라우드 기반 대용량 버전 관리 시스템&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h3&gt;핵심 특징&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;히스토리 분리&lt;/td&gt;
&lt;td&gt;Git에는 메타데이터만 기록, 실제 파일은 별도 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Actions 통합&lt;/td&gt;
&lt;td&gt;자동으로 대용량 파일을 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비용 효율적&lt;/td&gt;
&lt;td&gt;자체 요금제 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;확장성 우수&lt;/td&gt;
&lt;td&gt;AI 모델, 이미지, 동영상 등 대규모 파일 관리에 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;  Lens 설치 및 설정&lt;/h2&gt;
&lt;h3&gt;1️⃣ Lens CLI 설치&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm install -g @uselens/cli&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또는&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn global add @uselens/cli&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;2️⃣ 프로젝트 초기화&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;lens init&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;3️⃣ 추적할 파일 등록&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;lens track &amp;quot;*.mp4&amp;quot;
lens track &amp;quot;*.safetensors&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;4️⃣ 커밋 및 푸시&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git add .
git commit -m &amp;quot;Add large model files&amp;quot;
git push origin main&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;  GitHub 연동 구조&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;[로컬 Git] → [GitHub 저장소] + [Lens 서버 연동] → [대용량 파일 Lens 클라우드 저장]&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.lenskeep&lt;/code&gt; 파일에 대용량 추적 경로 정의&lt;/li&gt;
&lt;li&gt;GitHub Actions로 Lens 전송 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  Lens vs Git LFS&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;Git LFS&lt;/th&gt;
&lt;th&gt;Lens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;저장 방식&lt;/td&gt;
&lt;td&gt;Git 히스토리에 LFS 포인터 기록&lt;/td&gt;
&lt;td&gt;해시 기반 별도 클라우드 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub 연동&lt;/td&gt;
&lt;td&gt;공식 지원&lt;/td&gt;
&lt;td&gt;GitHub App 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;무료 플랜&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;사용량 기반 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파일 크기 제한&lt;/td&gt;
&lt;td&gt;100MB 제한&lt;/td&gt;
&lt;td&gt;상대적으로 자유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;활용 분야&lt;/td&gt;
&lt;td&gt;이미지, 사운드&lt;/td&gt;
&lt;td&gt;AI 모델, 3D 파일, .safetensors 등&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;  실무 적용 팁&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;팀원들도 &lt;code&gt;lens install&lt;/code&gt; 필요&lt;/li&gt;
&lt;li&gt;GitHub 저장소 설정에서 Lens 앱 등록 필수&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lens push&lt;/code&gt;, &lt;code&gt;lens pull&lt;/code&gt; 자동화 가능 (GitHub Actions)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS공부/기타 CS지식</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/86</guid>
      <comments>https://stquokka.tistory.com/86#entry86comment</comments>
      <pubDate>Mon, 9 Jun 2025 23:38:04 +0900</pubDate>
    </item>
    <item>
      <title>백준 2248 - 이진수 찾기(Java)</title>
      <link>https://stquokka.tistory.com/85</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;  문제 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;길이가 N인 이진수 중에서 1의 개수가 L 이하인 이진수들을 &lt;b&gt;사전 순으로 정렬&lt;/b&gt;했을 때, K번째 이진수를 구하는 문제.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  문제 접근&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 모든 이진수를 직접 만들어서 K번째를 찾기에는 비효율적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 아이디어는 조합을 이용해 가능한 경우의 수를 계산하며 자릿수를 결정&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  알고리즘 설계&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 조합 미리 계산&lt;/h3&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;comb[n][k] = nCk (n개 중 k개를 고르는 경우의 수)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파스칼 삼각형을 활용하여 조합을 미리 계산합니다.&lt;/p&gt;
&lt;pre class=&quot;matlab&quot;&gt;&lt;code&gt;for (int i = 0; i &amp;lt;= 32; i++) {
    comb[i][0] = comb[i][i] = 1;
    for (int j = 1; j &amp;lt; i; j++) {
        comb[i][j] = comb[i - 1][j - 1] + comb[i - 1][j];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 자리수 별 분기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;i번째 자리를 '0'으로 채울 경우 &amp;rarr; 나머지 자리에서 1이 L개 이하인 경우의 수 = comb[i - 1][0] + ... + comb[i - 1][L]&lt;/li&gt;
&lt;li&gt;만약 이 값보다 K가 작거나 같다면 현재 자리는 '0'&lt;/li&gt;
&lt;li&gt;아니라면 현재 자리는 '1', K에서 해당 경우의 수만큼 빼고, L--&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  전체 코드&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static int N, L;
    static long K;
    static long[][] comb = new long[33][33];

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken()); // 이진수 길이
        L = Integer.parseInt(st.nextToken()); // 1의 개수 제한
        K = Long.parseLong(st.nextToken());   // K번째 이진수

        initComb(); // 조합 테이블 초기화

        StringBuilder result = new StringBuilder();
        int onesLeft = L;
        long order = K;

        for (int i = N; i &amp;gt; 0; i--) {
            long zeroCount = countValid(i - 1, onesLeft); // 앞자리를 0으로 시작할 수 있는 경우의 수

            if (order &amp;lt;= zeroCount) {
                result.append('0');
            } else {
                result.append('1');
                order -= zeroCount;
                onesLeft--;
            }
        }

        System.out.print(result);
    }

    // 조합 계산 (nCk)
    static void initComb() {
        for (int i = 0; i &amp;lt;= 32; i++) {
            comb[i][0] = 1;
            comb[i][i] = 1;
        }

        for (int i = 1; i &amp;lt;= 32; i++) {
            for (int j = 1; j &amp;lt; i; j++) {
                comb[i][j] = comb[i - 1][j - 1] + comb[i - 1][j];
                if (comb[i][j] &amp;gt; 1_000_000_000) comb[i][j] = 1_000_000_000; // overflow 방지
            }
        }
    }

    // i자리 이하에서 1이 max개 이하인 이진수 개수
    static long countValid(int digit, int maxOne) {
        long sum = 0;
        for (int i = 0; i &amp;lt;= maxOne; i++) {
            sum += comb[digit][i];
        }
        return sum;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 시간복잡도&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;단계&lt;/th&gt;
&lt;th&gt;복잡도&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;조합 계산&lt;/td&gt;
&lt;td&gt;O(N^2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이진수 생성&lt;/td&gt;
&lt;td&gt;O(N)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순하게 모든 경우를 만드는 것이 아니라, &lt;b&gt;조합으로 경우의 수를 세며 사전 순으로 결정&lt;/b&gt;해야 하는 문제&lt;/li&gt;
&lt;li&gt;이진수 생성 문제에서 자주 쓰이는 대표 패턴&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>알고리즘</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/85</guid>
      <comments>https://stquokka.tistory.com/85#entry85comment</comments>
      <pubDate>Sat, 7 Jun 2025 22:38:39 +0900</pubDate>
    </item>
    <item>
      <title>collect(Collectors.toList()) vs Stream.toList()</title>
      <link>https://stquokka.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Java 16 이후, 자바에서는 &lt;code&gt;Stream.toList()&lt;/code&gt;라는 새로운 메서드를 제공하여 기존의 &lt;code&gt;collect(Collectors.toList())&lt;/code&gt; 방식을 대체할 수 있게 되었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 핵심 차이점 요약&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;&lt;code&gt;collect(Collectors.toList())&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;Stream.toList()&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;도입 버전&lt;/td&gt;
&lt;td&gt;Java 8 이상&lt;/td&gt;
&lt;td&gt;Java 16 이상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반환 타입&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ArrayList&lt;/code&gt; or other mutable list&lt;/td&gt;
&lt;td&gt;&lt;b&gt;불변 리스트 (Immutable List)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;외부 라이브러리 의존&lt;/td&gt;
&lt;td&gt;필요 (&lt;code&gt;Collectors&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;불필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;변경 가능 여부&lt;/td&gt;
&lt;td&gt;&lt;b&gt;가능 (mutable)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;불가능 (immutable)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;스레드 안정성&lt;/td&gt;
&lt;td&gt;불안정&lt;/td&gt;
&lt;td&gt;불안정 (불변이지만 thread-safe 아님)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  예시 코드 비교&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Java 8 방식 - &lt;code&gt;Collectors.toList()&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

List&amp;lt;String&amp;gt; list = Stream.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)
                          .collect(Collectors.toList());

list.add(&quot;d&quot;); // 가능
System.out.println(list); // [a, b, c, d]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Java 16 방식 - &lt;code&gt;Stream.toList()&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import java.util.List;
import java.util.stream.Stream;

List&amp;lt;String&amp;gt; list = Stream.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)
                          .toList();

list.add(&quot;d&quot;); // UnsupportedOperationException 발생&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚠️ 주의할 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;toList()&lt;/code&gt;로 생성된 리스트는 &lt;b&gt;불변(immutable)&lt;/b&gt; 이므로 &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;remove&lt;/code&gt; 등의 조작이 불가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Collectors.toList()&lt;/code&gt;는 매번 새로운 &lt;code&gt;ArrayList&lt;/code&gt;를 생성하며, &lt;b&gt;수정 가능한 리스트&lt;/b&gt;를 원할 때 사용합니다.&lt;/li&gt;
&lt;li&gt;프로젝트에서 Java 16 이상을 사용한다면, &lt;b&gt;불변 리스트가 필요한 경우에만 &lt;code&gt;toList()&lt;/code&gt;를 활용&lt;/b&gt;하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Java 8 이하&lt;/b&gt;: 무조건 &lt;code&gt;collect(Collectors.toList())&lt;/code&gt; 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Java 16 이상&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트 수정이 필요하면 &lt;code&gt;Collectors.toList()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;불변 리스트가 필요하면 &lt;code&gt;toList()&lt;/code&gt; 추천&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드 가독성 면에서는 &lt;code&gt;toList()&lt;/code&gt;가 간결하고 선언적입니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS공부/Java &amp;amp; Spring</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/84</guid>
      <comments>https://stquokka.tistory.com/84#entry84comment</comments>
      <pubDate>Sat, 7 Jun 2025 21:11:14 +0900</pubDate>
    </item>
    <item>
      <title>백준 10800번: 컬러볼 (Java)</title>
      <link>https://stquokka.tistory.com/83</link>
      <description>&lt;h2&gt;  문제 목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;각 공은 색과 크기를 가진다.&lt;/li&gt;
&lt;li&gt;모든 공 쌍에 대해 &lt;strong&gt;서로 다른 색&lt;/strong&gt;이면서 &lt;strong&gt;작은 공&lt;/strong&gt;의 크기만큼 점수를 획득한다.&lt;/li&gt;
&lt;li&gt;각 공이 얻을 수 있는 총 점수를 출력한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 풀이 전략 요약&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ball 객체 정의&lt;/strong&gt;&lt;br&gt;→ &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;index&lt;/code&gt;를 저장하는 클래스 생성&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;정렬 기반 누적합 알고리즘&lt;/strong&gt;&lt;br&gt;→ 공을 크기 기준으로 정렬하고, 같은 크기까지는 점수 계산을 미뤄둔다&lt;br&gt;→ 누적합을 활용해 총합과 색상별 합을 미리 관리한다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;투 포인터&lt;/strong&gt;&lt;br&gt;→ 현재 공보다 작은 공들을 전부 포함시키는 방식으로 점수 누적&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;색이 같은 경우 제외&lt;/strong&gt;&lt;br&gt;→ 같은 색의 합은 제외하여 점수 계산&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;  핵심 개념&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;변수&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;total&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;지금까지 본 공 중 크기가 현재 공보다 작은 공들의 전체 크기 합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;colorSum[color]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;특정 색상 공의 누적 크기 합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;result[i]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;i번 공이 얻을 수 있는 점수 (조건 만족하는 다른 공들의 크기 합)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;  전체 코드&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import java.io.*;
import java.util.*;

public class Main {
    static class Ball implements Comparable&amp;lt;Ball&amp;gt; {
        int color, size, index;
        Ball(int color, int size, int index) {
            this.color = color;
            this.size = size;
            this.index = index;
        }

        @Override
        public int compareTo(Ball o) {
            return this.size - o.size;
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        Ball[] balls = new Ball[n];
        int maxColor = 0;

        for (int i = 0; i &amp;lt; n; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int c = Integer.parseInt(st.nextToken());
            int s = Integer.parseInt(st.nextToken());
            balls[i] = new Ball(c, s, i);
            maxColor = Math.max(maxColor, c);
        }

        Arrays.sort(balls);  // 크기 기준 정렬

        int[] result = new int[n];
        int[] colorSum = new int[maxColor + 1];
        int total = 0, j = 0;

        for (int i = 0; i &amp;lt; n; i++) {
            Ball cur = balls[i];

            // 현재 공보다 작은 크기 공들의 누적합 업데이트
            while (balls[j].size &amp;lt; cur.size) {
                total += balls[j].size;
                colorSum[balls[j].color] += balls[j].size;
                j++;
            }

            // 총합 - 같은 색상 누적합
            result[cur.index] = total - colorSum[cur.color];
        }

        for (int r : result) System.out.print(r + &amp;quot;\n&amp;quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  정리&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;정렬 후 투 포인터 방식으로 누적합을 갱신하면서 탐색&lt;/li&gt;
&lt;li&gt;같은 색은 점수에 포함하지 않도록 색상별 합을 따로 관리&lt;/li&gt;
&lt;li&gt;복잡한 브루트포스보다 훨씬 효율적인 누적합 전략&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>알고리즘</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/83</guid>
      <comments>https://stquokka.tistory.com/83#entry83comment</comments>
      <pubDate>Wed, 4 Jun 2025 23:50:01 +0900</pubDate>
    </item>
    <item>
      <title>백준 2294: 동전2(Java)</title>
      <link>https://stquokka.tistory.com/82</link>
      <description>&lt;h2&gt;  문제 요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;n가지 종류의 동전을 가지고 있음&lt;/li&gt;
&lt;li&gt;각각의 동전은 중복해서 사용할 수 있음&lt;/li&gt;
&lt;li&gt;이 동전들을 이용해 총합이 &lt;code&gt;k&lt;/code&gt;원이 되도록 할 때,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;필요한 동전의 최소 개수&lt;/strong&gt;를 구하라  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;불가능한 경우 -1을 출력&lt;/strong&gt;하라&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  문제 분석&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;전형적인 &lt;strong&gt;동전 최소 개수 DP 문제&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;점화식 기반으로 &lt;strong&gt;1차원 DP 테이블&lt;/strong&gt;을 구성하여 풀이&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  알고리즘 설계&lt;/h2&gt;
&lt;p&gt;1️⃣ &lt;strong&gt;DP 배열 정의&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dp[j]&lt;/code&gt; = j원을 만들기 위한 최소 동전 개수  &lt;/li&gt;
&lt;li&gt;초기값: &lt;code&gt;dp[0] = 0&lt;/code&gt;, 나머지는 &lt;code&gt;INF&lt;/code&gt; (큰 값)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2️⃣ &lt;strong&gt;점화식&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 동전 금액 &lt;code&gt;c&lt;/code&gt;에 대해&lt;br&gt;&lt;code&gt;dp[j] = min(dp[j], dp[j - c] + 1)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;3️⃣ &lt;strong&gt;최종 정답&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dp[k]&lt;/code&gt;가 &lt;code&gt;INF&lt;/code&gt;이면 만들 수 없는 금액 → &lt;code&gt;-1&lt;/code&gt; 출력  &lt;/li&gt;
&lt;li&gt;아니면 &lt;code&gt;dp[k]&lt;/code&gt; 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 코드 구현 (Java)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int n = Integer.parseInt(st.nextToken()); 
        int k = Integer.parseInt(st.nextToken()); 

        int[] coin = new int[n];
        for (int i = 0; i &amp;lt; n; i++) {
            coin[i] = Integer.parseInt(br.readLine());
        }


        int INF = k + 1;
        int[] dp = new int[k + 1];
        Arrays.fill(dp, INF);
        dp[0] = 0;

        for (int i = 0; i &amp;lt; n; i++) {
            int c = coin[i];
            for (int j = c; j &amp;lt;= k; j++) {
                dp[j] = Math.min(dp[j], dp[j - c] + 1);
            }
        }

        System.out.println(dp[k] &amp;gt; k ? -1 : dp[k]);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;⏱️ 시간 복잡도&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;O(N × K)  &lt;/li&gt;
&lt;li&gt;N: 동전 종류 수 (최대 100), K: 금액 (최대 10,000)&lt;/li&gt;
&lt;li&gt;10^6 이내이므로 충분히 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  정리&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;각 금액마다 최소 동전 개수를 메모이제이션 방식으로 저장&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모든 동전으로 K까지 누적 계산&lt;/strong&gt;하여 최솟값을 구하는 방식&lt;/li&gt;
&lt;li&gt;2중 for문이지만 내부 연산이 매우 작고 빠름&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>알고리즘</category>
      <author>quokkaST</author>
      <guid isPermaLink="true">https://stquokka.tistory.com/82</guid>
      <comments>https://stquokka.tistory.com/82#entry82comment</comments>
      <pubDate>Mon, 2 Jun 2025 21:33:07 +0900</pubDate>
    </item>
  </channel>
</rss>