안경잡이개발자

728x90
반응형

  암호가 걸려 있는 PDF 파일의 암호를 알고 있을 때 암호를 물어보지 않도록 보안 설정을 바꾸는 방법을 소개하고자 합니다. (참고로 이 포스팅은 암호가 걸려 있는 PDF 파일의 암호를 뚫는 방법은 아닙니다.)

 

  예를 들어 구글 크롬(Chrome) 브라우저를 이용해 암호가 걸려 있는 PDF를 열 때는 다음과 같이 비밀번호를 입력해야 합니다.

 

 

  파일을 연 뒤에는 프린트(Print) 버튼을 눌러서 다른 형식의 PDF로 저장하면 됩니다.

 

 

  다음과 같이 [PDF로 저장]을 눌러서 PDF로 다시 저장할 수 있도록 합니다.

 

 

  이렇게 저장된 PDF 파일에는 암호가 걸려있지 않게 됩니다.

 

728x90
반응형

728x90
반응형

  Apktool을 이용해 APK 파일을 리버싱한 뒤에 이를 다시 재빌드(Rebuild)할 때 가끔 다음의 오류를 만날 수 있다.

 

brut.androlib.AndrolibException: brut.common.BrutException: could not exec (exit code = 1)

 

  이 오류는 Apktool을 이용해 리패키징을 진행할 때 마주칠 수 있는 오류이다. 이 오류는 대부분 AAPT 관련 이슈로 발생한다. AAPT(Android Asset Packaging Tool)안드로이드에서 사용되는 문자열, 이미지 등의 에셋(Asset)을 효율적으로 컴파일하고 패키징하기 위한 도구이다. 안드로이드 앱을 개발할 때는 별도의 에셋 폴더에 문자열, 이미지를 모아 놓는데 이것이 실제 코드와 함께 동작할 수 있도록 한다.

 

방법 1) 리패키징할 때 AAPT v2 사용 강제하기

 

  2020년 기준으로 최신 앱들은 AAPT v2를 사용하는 경우가 많은데, Apktool의 버전에 따라서 이를 처리하는 방법이 다르다. 예를 들어 Apktool v.2.3.4는 AAPT v1과 v2 바이너리를 모두 묶어서 .jar 파일에 넣는다. 이러한 과정에서 리패키징 오류가 발생할 수 있는데, AAPT v2 사용을 강제한다면 문제가 해결될 가능성이 있다.

 

  필자의 경우 Apktool v.2.4.1을 이용하였으며, 재빌드 과정에서 --use-aapt2 옵션을 넣어주었다. 일단 먼저 디컴파일을 하면 다음과 같이 실행될 것이다. 정상적으로 디컴파일이 완료된 것을 확인할 수 있다.

 

apktool -f d test.apk -o test_aapt_v2

 

 

  이후에 재빌드를 할 때 AAPT v2를 강제하지 않으면 다음과 같은 오류가 발생할 수 있다. 그것도 매우 많은 오류가 한꺼번에 발생할 수 있다. 아마 대부분 아래와 같은 오류를 만나서 헤맬 것이다.

 

apktool -f b test_aapt_v2

 

 

  이때 다음과 같이 AAPT v2 사용을 강제하면, 오류가 해결될 가능성이 있다.

 

apktool -f b test_aapt_v2 --use-aapt2

 

  다만 일부 앱에서는 AAPT v2를 강제하여도 오류가 남아 있거나 새로운 오류가 등장할 수 있다. 필자의 경우에도 여전히 오류가 남아 있어서, 다른 방법을 찾아야 했다. --use-aapt2 옵션을 추가하여 문제가 해결된 사람들은 여기에서 끝내도 된다.

 

 

방법 2) Apktool 버전 바꾸어 보기

 

  Apktool의 버전을 교체해본다. 필자의 경우 Apktool 최신 버전(Latest)인 2.4.1을 이용했는데, 이전 버전인 Apktool 2.3.4를 함께 이용해 보는 것도 방법이다. 포럼의 일부 게시글에서는 디컴파일과 리패키징에서 사용하는 Apktool의 버전을 다르게 하라는 조언이 있으며, 이를 통해 해결했다는 사례를 찾을 수 있었다.

 

  혹은 아예 더 낮은 Apktool 버전을 사용하면 일부 앱에 대해서는 정상 동작한다는 사례를 찾을 수 있었다. 따라서 자신의 앱에 대하여 오류가 발생한다면, 여러 가지 버전의 Apktool을 적용해보자. 참고로 필자의 경우에 이 방법을 사용해도 리패키징에서 실패하였다.

 

방법 3) Java 버전 바꾸어 보기

 

  포럼에서는 32-bit로 설치된 Java를 제거하고, 64-bit Java를 설치했더니 문제가 해결된 사례가 있었다고 한다. 필자의 경우 이건 적용해보지 않았다.

 

방법 4) 디컴파일 과정에서 리소스 제거하기

 

  만약 애초에 발생한 오류가 리소스(Resource) 컴파일 과정에서 발생한 것이라면, 리소스는 제외하고 디코딩을 한다면 나중에 리패키징을 할 때 오류가 발생하지 않는다. 소스코드를 수정하는 과정에서 리소스는 건드리지 않고 Java (smali) 코드만 수정하는 것이 목표인 경우, 이 방법만으로도 오류 없이 분석을 진행할 수 있다.

 

  공식 문서에 따르면 다음과 같이 기술되어 있다.

 

  "This will prevent the decompile of resources. This keeps the resources.arsc intact without any decode. If only editing Java (smali) then this is the recommended action for faster decompile & rebuild."

 

  여기에서 resources.arsc 각종 리소스 정보를 담고 있는 파일이다. 우리가 앱을 수정할 때 리소스 파일을 수정하게 되는 일이 필요할 수 있는데, 이때는 이 파일을 디코딩하여 우리가 분석할 수 있는 형태로 변환해야 한다. 예를 들어 새로운 이미지를 추가하거나, 문자열을 추가할 때 꼭 필요할 것이다. 만약 이런 행위 없이, 취약점 분석이 목적이라면 사실 resources.arsc 디코딩은 필요하지 않을 것이다.

 

  저자의 경우 취약점 분석이 목적이므로, 리소스를 제외하고 디코딩을 수행했다.

 

apktool -f d test.apk -o test_without_resource -r

 

 

  이 경우에 다시 리패키징을 할 때 오류가 발생하지 않는다.

 

apktool b test_without_resource

 

 

  필자는 앱을 분석할 때 리소스를 직접 추가하여 앱을 확장할 목적이 없고, 단순히 smali 코드 수정 및 분석이 주 목적이어서 이러한 방법을 이용해도 문제가 없는 상황이다. 결과적으로 다음과 같이 dist 폴더 안에 APK 파일이 생성된다.

 

 

※ 요약 ※

 

  깃허브와 포럼에서 찾아 본 결과, 해결 가능성이 있다고 제안된 방법들은 다음과 같다.

 

  1. 빌드할 때 --use-aapt2 옵션 넣어서, AAPT v2 사용 강제하기
  2. 빌드할 때 이전 Apktool 버전 사용해보기 (디컴파일 때와 다른 버전으로)
  3. Java 버전 및 운영체제 환경 바꾸어서 다시 빌드하기 (32bit -> 64bit)
  4. 리소스 수정이 필요하지 않은 경우, 디컴파일 할 때 -r 옵션 넣어서 resources.arsc 파일 제외하고 디코딩하기

 

  단, 앱 리소스 수정이 꼭 필요하면 1, 2, 3번 적용해보면서 문제 해결하야 할 것이다. 다만 취약점 분석이 목적이라면 대개 resources.arsc를 갈아 끼울 필요는 없고, smali 코드 수정이 필요한 정도라서 4번으로 진행해도 될 것이다.

728x90
반응형

728x90
반응형

  Apktool은 대표적인 안드로이드 애플리케이션(Android Application) 대상의 리버스 엔지니어링 도구입니다. APK 파일을 분석하여 리소스를 뽑아낼 수 있고(디코딩), 코드를 수정하여 다시 재빌드(Rebuild)할 수도 있습니다. 실제로 XML, 이미지 파일, .dex 파일을 포함하여 안드로이드 앱의 주요 소스를 뽑기 위해 가장 많이 사용되고 있습니다.

 

  ▶ Apktool 다운로드: ibotpeaches.github.io/Apktool/

 

  설치를 위해서 [Install] 페이지로 이동하고, 자신의 운영체제에 맞는 프로그램을 다운로드 받습니다.

 

 

  ▶ Apktool.jar 다운로드: bitbucket.org/iBotPeaches/apktool/downloads/

 

  설명에 나와 있는대로 차근차근 다운로드 및 세팅을 진행하시면 됩니다. 가장 먼저 Apktool은 아래와 같이 다운로드 가능합니다.

 

 

  이때 파일의 버전명을 지우고 이름을 apktool.jar로  바꾸어서 C:\\windows 경로에 두면 됩니다. 그리고 apktool.bat 파일 또한 만들어서 C:\\windows 위치에 둡니다. apktool.bat 파일의 내용은 다음의 경로에서 확인할 수 있습니다.

 

  ▶ apktool.bat 내용: https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/windows/apktool.bat

 

  최종적으로 다음과 같은 형태가 됩니다.

 

 

  이후에 명령 프롬프트를 실행하여 apktool 이라고 입력하시면, 정상적으로 툴이 실행됩니다.

 

 

※ 참고사항 ※

 

  참고로 Apktool은 버전에 따라서 APK 파일을 처리하는 방법이 다를 수 있기 때문에, 가능하면 최신 버전과 가장 많이 사용되는 버전 두 가지를 모두 이용하는 것이 좋습니다. 필자의 경우 최신 버전인 Apktool v2.4.1과 더불어 Apktool v.2.3.4를 같이 사용합니다. 저는 그래서 다음과 같이 apktool_2.3.4.jar 또한 다운로드 받아서 윈도우 폴더에 위치시킵니다.

 

 

  이 경우 직접 java를 이용하여 Apktool을 실행하실 수 있습니다.

 

java -jar C:\Windows\apktool_2.3.4.jar

 

 

※ 디컴파일 하는 방법 ※

 

  디컴파일(Decompile)을 위해 하나의 APK 파일을 준비한 뒤에 다음과 같이 명령어를 입력합니다. 여기에서 d는 특정한 파일을 디코딩(Decoding)하라는 의미입니다. 공식 문서에서는 Decoding이라는 표현을 쓰고 있네요. 참고로 -o 옵션을 넣어서 만들어질 소스 폴더의 경로를 설정할 수 있습니다. -o 옵션을 넣지 않으면 자동으로 앱 이름과 동일한 이름의 폴더가 만들어집니다.

 

apktool d {앱 파일} -o {소스 폴더 경로}

 

 

※ 재빌드 하는 방법 ※

 

  소스코드를 수정한 뒤에 다시 재빌드(Rebuild)할 때는 다음과 같은 명령어를 입력합니다. 여기에서 b는 특정한 폴더를 기준으로 다시 빌드(Build)를 진행한다는 의미입니다. 참고로 -o 옵션을 넣어서 빌드 결과로 만들어질 APK 파일의 경로를 설정할 수 있습니다.

 

apktool b {소스 폴더 경로} -o {앱 파일}

 

  재빌드가 성공적으로 이루어진다면 다음과 같이 APK 파일이 만들어지는 것을 확인할 수 있습니다.

 

 

※ 참고사항 ※

 

  추가적인 Apktool에 대한 사용 방법은 공식 문서를 참고하세요.

 

  ▶ 공식 문서: ibotpeaches.github.io/Apktool/documentation/

728x90
반응형

728x90
반응형

  최근에 방송용/녹화용 무료 소프트웨어로 가장 많이 사용되고 있는 것들로는 OBS, PRISM 등이 있습니다. 개인적으로 PRISM Live Studio은 사용 방법이 간단하고 사용자 친화적이라는 생각이 듭니다. (혹시 더 좋은 방송/녹화용 소프트웨어가 있다면 추천해주세요!) 아무튼 저는 최근에 방송이나 녹화 목적으로 PRISM을 이용하고 있습니다. 처음에는 네이버 밴드에서 강의 목적으로 PRISM을 이용했는데 지금은 이게 제일 편해졌습니다. 참고로 OBS도 무료이고 고급스러운 기능들을 제공합니다. 자기한테 가장 잘 맞는 것을 이용하시면 됩니다.

 

※ PRISM Live Studio 설치 방법 ※

 

  PRISM Live Studio는 아래 경로를 통해 설치하실 수 있습니다. (근데 왜 웹 사이트가 HTTPS가 아니라 HTTP를 쓰고 있는 거지... 기분 탓인가...)

 

  ▶ PRISM 설치 경로: prismlive.com/

 

PRISM Live Studio

멀티 플랫폼 라이브 방송과 다양한 영상 제작 및 편집 기능을 제공하는 크리에이터 툴

www.prismlive.com

 

  웹 사이트에 방문하여 바로 설치를 진행할 수 있습니다. 자신의 운영체제에 맞는 설치 프로그램을 자동으로 제안해 주네요.

 

 

  설치 프로그램을 실행하여 설치를 진행하실 수 있습니다. 기본적으로 [확인] 버튼만 눌러도 복잡한 설정 없이 설치를 진행하실 수 있습니다. 참고로 PRISM Live Studio를 이용하기 위해 회원가입/로그인 과정이 필요할 수 있습니다. 아무튼 하라는 대로만 진행하면 됩니다. 개인적으로 초보자 분들도 쉽게 따라할 수 있는 편입니다.

 

 

  다음과 같이 PRISM이 설치될 경로를 설정하여 설치를 진행할 수 있습니다.

 

 

  이어서 다음과 같이 설치가 진행되는 것을 알 수 있습니다.

 

 

  최종적으로 설치가 끝나면 다음과 같이 [시작] 버튼을 눌러서 시작을 진행할 수 있습니다.

 

 

※ 기본 설정 방법 ※

 

  기본적인 상태는 다음과 같습니다. 먼저 [장면 목록]에 여러 개의 장면이 들어갈 수 있습니다. 사실 기본적으로 주어지는 하나의 장면만 가지고도 웬만한 방송을 진행할 수 있습니다.

 

 

  가장 먼저 [소스 목록]에서 [모니터]를 추가해 보겠습니다. 모니터를 추가하면 현재 우리의 컴퓨터 모니터 화면을 방송/녹화 비디오에 송출할 수 있습니다.

 

 

  다음과 같이 송출하고자 하는 디스플레이를 선택할 수 있습니다. 저는 현재 하나의 모니터를 쓰고 있으며, 모니터 화면 해상도는 1920 X 1080입니다. 이것 그대로 디스플레이 선택이 가능한 것을 알 수 있습니다. 그러면 제 화면에 보이는 내용 전체가 그대로 송출됩니다.

 

 

    또한 마이크를 사용하고자 한다면, 가장 먼저 컴퓨터에 마이크를 연결합니다. 이후에 [마이크/보조] 부분을 우클릭하여 [속성]에 들어가도록 합니다.

 

 

  이어서 자신이 연결한 마이크를 선택하면 됩니다.

 

 

  이제 본격적으로 방송이나 녹화를 진행하기 전에 전체 환경 설정을 진행할 필요가 있습니다. 가장 먼저 선택해야 할 것은 방송/녹화 비디오의 해상도입니다. 최소한 1920 X 1080 (HD Quality) 이상의 해상도를 선택하는 것이 좋습니다. 요즘에는 HD보다 낮은 화질인 경우 저화질 취급을 받기 때문입니다. 참고로 자신의 컴퓨터 사양에 따라서 높은 해상도를 선택하지 못할 수도 있습니다.

 

 

  이후에 가장 중요한 부분을 설정해 주겠습니다. 바로 [방송] 및 [녹화] 부분에서 인코더(Encoder)를 설정하는 것입니다. 저는 일반적으로 다음과 같이 방송과 녹화 목적으로 모두 소프트웨어 (x264)를 선택하여 CPU로 녹화를 진행합니다. 여기에서 소프트웨어 (x264)는 컴퓨터의 CPU를 의미하는데요. 제 컴퓨터는 CPU 성능이 좋은 편이기 때문입니다.

 

 

  참고로 인코더는 소프트웨어와 하드웨어로 나누어지는데, 이때 하드웨어 (QSV)는 내장 그래픽 카드를 이용하는 경우이고, 추가적으로 NVIDIA 그래픽 카드가 있다면 하드웨어 (NVENC) 그래픽 카드도 선택할 수 있을 것입니다. 기본적으로 소프트웨어가 아닌 하드웨어를 선택하게 되면 GPU를 이용하므로 상대적으로 CPU를 적게 사용하여 게임이나 강의 녹화를 진행할 수 있게 됩니다.

 

  단, 자기가 사용하는 프로그램이나 컴퓨터 환경에 따라서 효율적인 선택지가 다를 수 있습니다. 여러 번 선택을 해보면서 가장 수월하게 방송/녹화가 진행되는 경우를 채택하시면 됩니다.

 

 

  예를 들어 게임을 GPU로 돌릴 때는, 녹화 프로그램(PRISM)으로 소프트웨어(CPU)를 선택하는 방식을 사용할 수 있습니다. 혹은 반대로 이용할 수도 있어요. 그래서 녹화된 영상이 뚝뚝 끊기거나 오류가 발생하는 경우에는 여러 번 선택을 해보시면서 가장 좋은 조합을 찾으셔야 합니다.

 

  이제 실제로 방송을 보는 사람 입장에서의 화면을 설정합시다. 이후에 [지금 적용] 화살표 버튼을 눌러서 편집 중인 영상(EDIT) 부분이 실제 방송용 혹은 녹화용 화면으로 나오도록 만드시면 됩니다.

 

 

※ 녹화하는 방법 ※

 

  녹화를 진행할 때에는 [Rec] 버튼을 누르시면 됩니다. 녹화를 마치면 자신이 미리 설정했던 경로에 녹화된 동영상 파일이 저장됩니다.

 

 

※ 실시간 방송 하는 방법 ※

 

  또한 실시간 방송을 진행할 때에는 가장 먼저 [채널 추가]를 누르시면 됩니다.

 

 

  다음과 같이 다양한 채널을 선택할 수 있습니다.

 

 

  참고로 많은 방송 플랫폼은 고유하게 부여되는 스트림 키(Stream Key)만 가지고도 방송을 진행할 수 있도록 합니다. 그래서 스트림 키가 노출된다면, 다른 사람이 제 계정으로 방송을 할 수도 있는 겁니다. 혹은 해당 플랫폼 계정으로 로그인을 한 뒤에 자동으로 PRISM과 연동이 되는 방식으로 동작하는 플랫폼도 존재합니다.

 

  아무튼 유튜브(YouTube)의 경우 다음과 같이 스트림 키를 입력하여 유튜브 채널로 실시간 방송을 송출할 수 있습니다.

 

 

  이후에 동시에 송출하고자 하는 채널들을 선택한 뒤에 [Go Live] 버튼을 누르면 실제로 라이브 방송이 진행됩니다.

 

728x90
반응형

728x90
반응형

(이번 대회는 대회 중간에 문제 오류로 인하여 Unrated 판정 공지가 나왔다. 그래서 A번과 B번만 풀었다.)

 

대회 링크: codeforces.com/contests/1418

A번 문제: codeforces.com/contest/1418/problem/A

 

  하나의 횃불(Torch)을 만들기 위해서는 1개막대(Stick)1개석탄(Coal)이 필요하다.

 

  우리는 처음에 1개의 막대기를 가지고 시작하며, 두 종류의 연산을 사용할 수 있다.

 

  1번) 1개의 막대기를 x개의 막대기로 교환

  2번) y개의 막대기를 1개의 석탄으로 교환

 

  이때 총 k개의 횃불(Torch)을 만들기 위해 필요한 최소 연산 횟수를 구하면 되는 문제다.

 

[ 문제 해설 ]

 

  우리는 먼저 1개의 막대기에서 시작한다. 따라서 1번 연산을 많이 사용해서 막대기의 개수를 최대한 늘린 뒤에, 그 다음에 2번 연산을 많이 사용해서 석탄을 얻어서 결과적으로 횃불을 만들면 된다.

 

  우리가 막대기를 처음에 증가시킬 때, 연산 1번을 사용할 때마다 막대기를 (x - 1)개씩 증가시킬 수 있다.

 

  k개의 횃불을 만들기 위해서는 k개의 막대기와 k개의 석탄이 필요하다. 이때 k개의 석탄을 만들기 위해서는 y * k개의 막대기가 필요하다. 즉, k개의 횃불을 만들기 위해서(y + 1) * k개의 막대기가 필요한 것이다.

 

  그렇다면 (y + 1) * k개 이상의 막대기를 얻기 위해 1번 연산을 얼마나 사용해야 할까? 바로 다음의 부등식을 만족시키는 한에서 최소한으로 사용해야 할 것이다.

 

  ▶ 필요한 1번 연산의 횟수 * (x - 1) + 1 >= (y + 1) * k

 

  따라서 다음의 코드를 통해서 1번 연산의 횟수의 최솟값을 구할 수 있다.

 

if ((y + 1) * k - 1) % (x - 1) == 0:
    one = ((y + 1) * k - 1) // (x - 1)
else:
    one = ((y + 1) * k - 1) // (x - 1) + 1

 

  이제 2번 연산을 k번 수행하면 최종적으로 k개의 횃불을 만들 수 있다. 따라서 최종적인 코드는 다음과 같다.

 

for _ in range(int(input())):
    x, y, k = map(int, input().split())
    if ((y + 1) * k - 1) % (x - 1) == 0:
        one = ((y + 1) * k - 1) // (x - 1)
    else:
        one = ((y + 1) * k - 1) // (x - 1) + 1
    two = k
    print(one + two)

 

B번 문제: codeforces.com/contest/1418/problem/B

 

  N개의 정수를 담고 있는 수열이 있다. N개 중에서 몇 개의 원소는 고정되어(Locked) 위치를 변경할 수 없다. 반면에 고정되어 있지 않은(Unlocked) 원소들은 위치를 서로 변경할 수 있다.

 

  이때 누적 합(Prefix Sum)이 0 미만인 인덱스(index) 중에서 최솟값(Miminum)인 k를 계산해야 한다. 만약 누적합이 0 미만인 인덱스가 없다면 k = 0가 된다.

 

  예를 들어 [-8, 4, -2, -6, 4, 7, 1]이 있다고 해보자. 여기에서 밑줄이 쳐진 원소들은 고정된 원소들이다. 이 경우 [-8, 4, 1, -2, 4, 7, -6]로 수열을 바꾸게 되면, 누적합은 [-8, -4, -3, -5, -1, 6, 0]이다. 이때, 누적합이 0 미만인 인덱스 중에서 최솟값은 k = 5가 된다. (인덱스를 1부터 센다고 했을 때)

 

[ 문제 해설 ]

 

  이 문제는 위치가 고정된 원소를 제외하고, 남은 원소들만 내림차순 정렬을 시켜주면 된다. 그때가 바로 누적 합(Prefix Sum)의 모든 원소가 가장 크게 형성되는 때라고 볼 수 있다.

 

from collections import deque

for _ in range(int(input())):
    n = int(input())
    data = list(map(int, input().split()))
    flag = list(map(int, input().split()))
    
    # 고정되지 않은 원소만 담아서 내림차순 정렬
    temp = []
    for i in range(n):
        if flag[i] == 0:
            temp.append(data[i])
    temp.sort(reverse=True)

    # 최종 결과 리스트 만들기
    result = []
    temp = deque(temp)
    for i in range(n):
        # 고정되지 않은 위치에는 내림차순으로 하나씩 넣기
        if flag[i] == 0:
            result.append(temp.popleft())
        # 고정되어 있다면 그대로 가져오기
        else:
            result.append(data[i])

    # 최종 결과 리스트 출력
    for x in result:
        print(x, end=' ')
    print()
728x90
반응형

728x90
반응형

  메일을 보내거나, 공적인 포럼에 글을 남길 때는 업로드 이전에 맞춤법을 검사하는 과정을 거치면 좋습니다. 저도 맞춤법 검사기를 쓴 지 얼마 안 되었는데, 이게 꽤 유용하다고 느낀 순간부터는 가능하면 맞춤법을 검사한 뒤에 글을 올리는 버릇을 가지게 되었습니다. 특히 이메일은 블로그 글과는 다르게 한 번 보내고 나면 수정이 안 되므로 이메일을 보내기 전에 맞춤법을 검사한 뒤에 전송하는 습관을 들이시면 유용합니다.

 

  특히 저는 대학원생이라서 포럼(Forum)에 글을 올리거나 교수님께 메일을 보내는 일이 많은데, 이러한 맞춤법 검사기를 쓰면 좋더라구요.

 

  ▶ 부산대학교 한국어 맞춤법/문법 검사기speller.cs.pusan.ac.kr/

 

  먼저 부산대학교 한국어 맞춤법/문법 검사기입니다. 이 사이트는 개인이나 학생인 경우 무료로 사용할 수 있습니다. 

 

  1) 맞춤법 오류가 존재하는 문장을 넣은 경우

 

  한 번 다음과 같이 일부러 맞춤법을 틀려 보도록 하겠습니다.

 

 

  그러면 다음과 같이 굉장히 맞춤법 오류를 잘 잡아주는 것을 알 수 있습니다.

 

 

  2) 맞춤법 오류가 없는 문장을 넣은 경우

 

  다음과 같이 맞춤법 오류가 없는 문장을 넣어보겠습니다.

 

 

  그러면 다음과 같이 맞춤법과 문법 오류를 찾지 못했다는 메시지가 출력됩니다.

 

 

  물론 맞춤법이 없다고 해서 문장 자체가 항상 전달력이 있고 좋다는 의미는 아닙니다. 단지 문법적인 오류가 없다는 의미라고 보시면 될 것 같습니다.

 

  ▶ 네이버 메일 맞춤법 검사 기능

 

  네이버 메일을 이용하시는 분은 간단히 메일을 보내기 전에 맞춤법 검사 기능을 이용할 수 있습니다. 아래와 같이 맞춤법 검사 버튼을 누르면 자동으로 검사를 진행합니다. 간단히 다음과 같이 맞춤법 오류가 있는 문장을 써보고, 검사를 진행해 보겠습니다.

 

 

  그러면 다음과 같이 맞춤법 오류를 잘 찾아줍니다.

 

728x90
반응형

728x90
반응형

  장치관리자를 실행하고자 하는데 "이 앱은 사용자 보호를 위해 차단되었습니다."라는 오류 메시지가 나오면서 장치 관리자가 실행되지 않는 경우가 발생할 수 있다. 참고로 [장치 관리자]mmc.exe라는 일종의 시스템 소프트웨어인데, 간혹 윈도우 운영체제를 사용하다가 mmc.exe 앱을 실행할 수 없다는 메시지가 출력되는 문제가 발생한 경우에 대한 해결 방법을 소개하고자 한다.

 

  이 문제는 대개 보안 옵션 문제 때문에 발생하는데, 먼저 명령 프롬프트를 [관리자 권한으로 실행]해보자.

 

 

  이후에 secpol.msc를 실행해주자. 이건 말 그대로 보안 정책(Security Policy)의 줄임말이다.

 

 

  이후에 [보안 설정] - [로컬 정책] - [보안 옵션] - [사용자 계정 컨트롤: 관리 승인 모드에서 모든 관리자 실행]을 우클릭하여 [속성(R)]에 들어간다.

 

 

  이제 다음과 같이 [로컬 보안 설정]에서 [사용 안 함]에 체크를 해줌으로써 보안 정책을 느슨하게 설정할 수 있다.

 

 

  이때 다음과 같이 "사용자 계정 컨트롤을 끄려면 컴퓨터를 다시 시작해야 합니다."라는 메시지가 출력되는 것을 알 수 있다.

 

 

  결과적으로 컴퓨터를 다시 시작하면 다음과 같이 정상적으로 장치 관리자가 실행된다.

 

 

728x90
반응형

728x90
반응형

대회 링크: atcoder.jp/contests/abc178

A번 문제: atcoder.jp/contests/abc178/tasks/abc178_a

 

  이 문제는 입력으로 1이 들어왔을 때 0을 출력하고, 입력으로 0이 들어왔을 때 1을 출력하면 되는 문제다.

 

[ 문제 해설 ]

 

  단순히 하라는 대로 구현하면 된다.

 

[ 정답 코드 ]

 

x = int(input())
 
if x == 0:
    print(1)
else:
    print(0)

 

B번 문제: atcoder.jp/contests/abc178/tasks/abc178_b

 

  a, b, c, d가 입력으로 들어왔을 때 a <= x <= b와 c <= y <= d가 성립하도록 x랑 y를 하나씩 뽑을 수 있다. 이때 만들 수 있는 x * y 중에서 가장 큰 값이 무엇인지 구하면 되는 문제다.

 

  단, a, b, c, d는 -10억부터 +10억 사이의 정수다.

 

[ 문제 해설 ]

 

  다음의 4가지 경우 중에서 가장 큰 값을 출력하면 된다.

 

  1) a * c

  2) a * d

  3) b * c

  4) b * d

 

[ 정답 코드 ]

 

a, b, c, d = map(int, input().split())
 
print(max(a * c, a * d, b * c, b * d))

 

C번 문제: atcoder.jp/contests/abc178/tasks/abc178_c

 

  길이가 n인 수열 {A1, A2, A3, ..., An}이 있을 때, 다음의 조건을 만족하는 수열의 개수를 세면 되는 문제다.

 

  ▶ 0 <= Ai <= 9

   값이 0인 원소가 하나 이상 존재한다.

   값이 9인 원소가 하나 이상 존재한다.

 

[ 문제 해설 ]

 

  이 문제는 N이 최대 1,000,000일 수 있으므로 O(N) 안쪽으로 해결해야 한다. 따라서 전형적인 DP를 이용해 문제를 해결할 수 있다.

 

  DP[n][0]: 길이가 N이면서 조건을 만족하는 수열의 개수 (단, 0의 개수: 0, 9의 개수: 0)

  DP[n][1]: 길이가 N이면서 조건을 만족하는 수열의 개수 (단, 0의 개수: 1 이상, 9의 개수: 0)

  DP[n][2]: 길이가 N이면서 조건을 만족하는 수열의 개수 (단, 0의 개수: 0, 9의 개수: 1 이상)

  DP[n][3]: 길이가 N이면서 조건을 만족하는 수열의 개수 (단, 0의 개수: 1 이상, 9의 개수: 1 이상)

 

  위와 같이 정의하여 해결할 수 있다.

 

  ... 그냥 더 쉽게 수학 공식으로 해결하자.

 

  ▶ 모든 경우의 수: 10^n

  ▶ 9를 제외한 경우의 수: 9^n

  ▶ 0을 제외한 경우의 수: 9^n

  ▶ 0과 9 둘 다 없을 때의 경우의 수: 8^n

 

  따라서 전체 경우의 수는 10^n - 9^n - 9^n + 8^n이다. 이를 포함배제의 원리(Principle of inclusion-exclusion)라고도 한다.

 

[ 정답 코드 ]

 

  참고로 값이 매우 매우 커질 수 있으나 파이썬(Python)의 경우 이를 효과적으로 처리한다.

 

n = int(input())
mod = int(1e9) + 7

result = ((10 ** n) - 2 * (9 ** n) + (8 ** n)) % mod
print(result)

 

D번 문제: atcoder.jp/contests/abc178/tasks/abc178_d

 

  하나의 자연수 S가 있을 때, 다음의 조건을 만족하는 수열의 개수를 구하고자 한다.

 

  ▶ 모든 원소가 3 이상의 정수

  ▶ 모든 원소의 합이 S

 

  예를 들어 S = 7이라고 하면 {7], {3, 4}, {4, 3}이 있으므로 답은 3이다.

 

[ 문제 해설 ]

 

  이 문제는 S가 최대 2,000이므로 이차 시간 안에 동작하는 알고리즘을 고안해야 한다. 또한 최적 부분 구조(Optimal Substructure)가 성립하므로 DP를 이용해 문제를 해결할 수 있다. 예를 들어 S = 7의 모든 경우의 수는 다음의 모든 경우를 고려한 것과같다.

 

  ▶ {7}

  ▶ {3}에 DP[4]의 모든 수열을 이어 붙인 것들

  ▶ {4}에 DP[3]의 모든 수열을 이어 붙인 것들

  ▶ {5}에 DP[2]의 모든 수열을 이어 붙인 것들

  ▶ {6}에 DP[1]의 모든 수열을 이어 붙인 것들

 

  이런 아이디어를 고려하면 다음과 같은 코드로 문제를 해결할 수 있다.

 

[ 정답 코드 ]

 

s = int(input())
d = [0] * 2001

# 일단 기본적인 초기 값들 계산해보기
d[0] = 0
d[1] = 0
d[2] = 0
d[3] = 1 # {3}
d[4] = 1 # {4}
d[5] = 1 # {5}
d[6] = 2 # {6}, {3, 3}
d[7] = 3 # {6}, {3, 4}, {4, 3}
d[8] = 4 # {8}, {3, 5}, {4, 4}, {5, 3}
d[9] = 6 # {9}, {3, 6}, {4, 5}, {5, 4}, {6, 3}, {3, 3, 3}
 
for i in range(10, 2001):
    summary = 1 # 일단 자기 자신 하나를 원소로 삼는 경우
    for j in range(3, i):
        summary += d[i - j]
        summary = summary % (int(1e9) + 7)
    d[i] = summary
 
print(d[s])

 

E번 문제: atcoder.jp/contests/abc178/tasks/abc178_e

 

  2D 공간에 N개의 점(Point)이 존재한다. 이때 맨해튼 거리(Manhattan Distance)가장 먼 두 개의 점을 찾는 문제다. 예를 들어 i와 j 두 점이 있으면 맨해튼 거리는 |Xi - Xj| + |Yi - Yj|를 의미한다.

 

[ 문제 해설 ]

 

  먼저 Xi <= Xj라고 했을 때 다음의 두 경우로 나눌 수 있다.

 

  ▶ 만약 Yi <= Yj라면 거리는 (Xj + Yj) - (Xi + Yi)이다.

  ▶ 만약 Yi > Yj라면 거리는 (Xj - Yj) - (Xi - Yi)이다.

 

  이 문제는 잘 알려진 문제로, 맨해튼 거리이기 때문에 이러한 조건이 성립한다. 참고로 맨해튼 거리가 아니라 유클리드 거리라면 컨벡스 헐(Convex Hull)을 활용해야 제한 시간 안에 문제를 해결할 수 있다.

 

  첫 번째 경우를 보면 X + Y만 구하면 된다는 것을 알 수 있다.

  두 번째 경우를 보면 X - Y만 구하면 된다는 것을 알 수 있다.

 

  결과적으로 max(max(x + y) - min(x + y), max(x - y) - min(x - y))를 출력하면 된다.

 

[ 정답 코드 ]

 

n = int(input())
plus = []
minus = []
 
for i in range(n):
    x, y = map(int, input().split())
    plus.append(x + y)
    minus.append(x - y)
 
result = max(max(plus) - min(plus), max(minus) - min(minus))
print(result)
728x90
반응형

728x90
반응형

  윈도우(Windows) 운영체제를 제공하는 삼성 노트북은 별도로 Samsung Settings라는 이름의 앱을 제공하고 있습니다. 말 그대로 설정(Setting) 목적의 앱이라고 보시면 됩니다. 이 앱에서는 노트북의 기본적인 설정 기능을 제공하고 있습니다. 사실 이 앱의 존재 여부조차 모르는 경우가 많은데요. 다음과 같이 Samsung Settings 앱을 검색해서 실행해 볼 수 있습니다.

 

  참고로 삼성 노트북에서는 이 Samsung Settings는 일종의 통합 설정 프로그램으로 하드웨어 및 시스템 설정에 영향을 미칠 수 있기 때문에, 이 앱의 설정에 따라서 웹캠을 사용할 수 없는 등의 현상이 발생할 수 있는 것입니다.

 

 

  이 앱에서는 다양한 기능을 제공합니다.

 

  ▶ 시스템: 자동 부팅(절전 모드에서 덮개만 열어도 자동으로 부팅), 저소음 모드(팬 소음 줄이기)

  ▶ 전원 관리: 배터리 수명 연장을 위한 Battery Life Extender 기능

  ▶ 네트워크: 다양한 WiFi가 있을 때 최적의 무선 네트워크를 우선적으로 표시

  ▶ 디스플레이: 화면 밝기 조절(아웃도어 모드 등), 색감 설정 기능

  ▶ 음향 효과: 사용자의 환경에 적절한 음향 효과(표준 모드, 영화 모드 등) 설정 기능

  ▶ 입력 장치: 키보드 백라이트(어둡게, 밝게 등) 설정 기능

  ▶ 사생활 보호: 보안 레벨(카메라/마이크 전원 강제 차단 등) 설정 기능

 

  이 중에서 사생활 보호 기능이 바로 카메라와 마이크를 강제로 저지할 수 있습니다.

 

 

  위 그림을 보시면 녹화/녹음 방지 기능높음으로 설정되어 있는 것을 확인할 수 있습니다. 이 경우 하드웨어적으로 카메라와 마이크의 전원을 차단하기 때문에 다음과 같이 장치 관리자에 가도 이 하드웨어 장치가 현재 컴퓨터에 연결되어 있지 않다고 출력이 됩니다. 말 그대로 연결이 안 되어 있다는 거죠.

 

  그래서 장치에 오류가 있다고 오해를 할 수도 있습니다. '아니 내 노트북에 버젓이 카메라가 있는데 왜 없다는 거지?'라는 생각이 들 수 있어요.

 

 

  다음과 같이 방지 수준을 중간으로 설정하면 다시 장치 관리자에 카메라가 잡힙니다.

 

 

  장치 관리자에서 확인해보아도 다음과 같이 정상적으로 동작하는 것을 알 수 있습니다.

 

728x90
반응형

728x90
반응형

대회 링크: codeforces.com/contest/1407

A번 문제: codeforces.com/contest/1407/problem/A

 

  이 문제는 0 혹은 1로만 구성된 수열이 주어졌을 때, 순서를 유지한 상태로 최대 N / 2개의 원소를 삭제해서 다음의 식이 성립하도록 만들어야 한다. (N은 항상 2의 배수)

 

  a1 - a2 + a3 - a4 + ... = 0

 

[ 문제 해설 ]

 

  이 문제의 해결 아이디어는 꽤 간단하다. 먼저 수열에 들어 있는 0과 1의 개수를 각각 계산한다.

 

  ① 만약 0의 개수가 더 많거나 같다면 0의 개수만큼 출력하면 된다. ② 만약 1의 개수가 더 많다면 1의 개수만큼 출력해야 한다. 단, 1의 개수가 홀수인 경우에는 1개 적은 만큼 출력해야 한다.

 

  예를 들어 [1, 0, 1, 0, 0, 0]이 들어왔을 때에는 [0, 0, 0, 0]을 출력하면 된다. [1, 0, 1, 1, 1, 0]이 들어왔을 때에는 [1, 1, 1, 1]을 출력하면 된다.

 

[ 정답 코드 ]

 

for _ in range(int(input())):
    n = int(input())
    data = list(map(int, input().split()))
    zero = 0
    one = 0
    for x in data:
        if x == 0:
            zero += 1
        else:
            one += 1
    if zero >= one:
        print(zero)
        for i in range(zero):
            print(0, end=' ')
    else:
        print(one // 2 * 2) # 홀수인 경우 1이 더 작은 수를 이용
        for i in range(one // 2 * 2):
            print(1, end=' ')
    print()

 

B번 문제: codeforces.com/contest/1407/problem/B


  이 문제는 N개의 양의 정수를 담은 수열 a1, a2, a3, ..., an이 있을 때, 원소를 하나씩만 사용해서 수열 b1, b2, b3, ..., bn를 만드는 문제다. 이때 ci = GCD(b1, ..., bi)라고 하자. 우리는 수열 c1, c2, c3, ..., cn이 사전적으로 최댓값을 가지도록 하고 싶다.

 

  예를 들어 수열 A = {64, 25, 75, 100, 50}라고 하면, 정답 수열 B = {100, 50, 25, 75, 64}를 찾을 수 있다.

 

[ 문제 해설 ]

 

  이 문제는 완전 탐색으로 해결할 수 있다. 그냥 이중 반복문을 돌리면서 Maximum을 찾으면 된다. 다만, GCD(x, y, z) = GCD(GCD(x, y), z)라는 점을 알고 있어야 한다. 또한 0은 모든 정수의 배수라는 점에서 GCD(0, x) = x라는 점을 기억하자. 사실 이는 잘 알려진 공식이므로 이를 이용하여 쉽게 문제를 해결할 수 있다. 참고로 GCD 함수는 로그 복잡도를 가지므로 빠르게 동작한다.

 

[ 정답 코드 ]

 

import math
 
for _ in range(int(input())):
    n = int(input())
    data = list(map(int, input().split()))
    gcd_value = 0
    for i in range(n):
        max_value = (0, 0) # (현재 GCD 값, index)를 한꺼번에 묶어서 저장
        for j in range(n):
            if data[j] != -1: # 아직 출력하지 않은 데이터 중에 GCD 값을 가장 크게 만드는 데이터 찾기
                max_value = max(max_value, (math.gcd(gcd_value, data[j]), j))
        gcd_value = max_value[0] # GCD 값 갱신
        print(data[max_value[1]], end=' ') # 해당 데이터 출력
        data[max_value[1]] = -1 # 해당 데이터 처리 완료
    print()

 

C번 문제: codeforces.com/contest/1407/problem/C


  이 문제는 1부터 N까지의 값을 하나씩 가지고 있는 하나의 순열(Permutation)을 찾는 문제다. 우리는 최대 2 * N만큼의 쿼리를 날려서 정보를 얻어올 수 있다.

 

  이때 ? i j라고 쿼리를 날리면, pi mod pj의 값을 알려준다.

 

[ 문제 해설 ]

 

  나도 이 문제를 풀면서 떠올리게 된 사실인데, 두 양의 정수가 있을 때 쿼리를 두 번 날리면 누가 더 큰 지를 알 수 있다. 예를 들어 서로 다른 양의 정수 x와 y가 있을 때 다음의 명제가 성립한다.

 

  x mod y > y mod x라면, ① x보다 y가 더 크며 ② x mod y = x이다.

 

  따라서 앞에서부터 두 수씩 묶어서 쿼리를 날려 본다. 쿼리를 날리면서 항상 몇 번째 수가 가장 큰 지를 저장하면서, 우리는 더 작은 수의 값을 매번 확실히 찾을 수 있다.

 

import sys
 
n = int(input())
 
answers = [-1] * (n + 1)
 
max_index = 1
next_index = 2
 
for i in range(n - 1):
    print("?", max_index, next_index)
    sys.stdout.flush()
    one = int(input())
    print("?", next_index, max_index)
    sys.stdout.flush()
    two = int(input())
    if one < two:
        answers[next_index] = two
    else:
        answers[max_index] = one
        max_index = next_index
    next_index += 1
 
answers[max_index] = n
 
print("!", end=' ')
for i in range(1, n + 1):
    print(answers[i], end=' ')
728x90
반응형