[문제][알고스팟]WILDCARD
2019. 2. 6. 15:16ㆍ[개발] 지식/알고리즘 문제풀이
3가지 형태의 풀이가 있다.
첫번째는 완전탐색(match). 동적계획법을 사용하지 않기 때문에 시간이 오래 걸린다.
두번째는 DP 적용버전 (dynamicMatch) 문제 풀이가 가능하다.
세번째는 DP 적용버전에 시간복잡도를 낮춘것. (dynamicMatch2)
풀이
- W, s 를 파라미터로 갖는 cache 배열을 만든다. 그리고 함수 진입시점에 값의 유무를 확인하고 있으면 리턴 없으면 이후 로직을 수행한다.
- 반복문(while)으로 패턴의 현재 문자와 파일명의 현재 문자가 매칭되는 경우 (?포함) w와 s를 늘려나간다. 만약 반복문을 빠져나간다면 패턴의 길이가 더 짧거나, 파일명의 길이가 더 짧거나, '*'을 만난 경우이다. 패턴의 길이가 더 짧지만 마지막에 '*'이 있는 경우도 이후 로직에서 처리가 가능하다.
- 반복문이 끝난 후, 패턴의 인덱스가 끝에 도달했고, 파일명의 인덱스도 끝에 도달했으면 정상적으로 매칭된 상태이므로 cache에 저장하고 리턴한다.
- 하지만 '*'로 끝난 경우 재귀 호출한다. 단 패턴 인덱스는 하나 늘리고, 파일명 인덱스는 반복문으로 늘려가면서 '*'이 포함하는 케이스를 하나씩 늘려가면서 모두 재귀호출한다.
- 나머지는 매칭 실패로 간주한다.
회고
- 파일명이 패턴과 매칭되는 경우를 제외하고는 전부 '*'로 끝나는 케이스만 처리해주면 된다는 점을 직관적으로 생각해낼 수 없었다. 그 외의 접근법은 혼자 풀려고 했을때와 유사했다.
- DP를 적용할때 인수를 w,s 두가지로 주면 참조적 투명 함수가 되는 것인지 와닿지 않았다. 혹은 참조적 투명함수가 되더라도 반복 호출된다는게 이해가 가지 않았다. 하지만 가장 쉬운 케이스로 손수 따라가보면 같은 인수의 함수가 반복 호출됨을 알 수 있었다. 그리고 하나의 패턴에 하나의 파일명에 대해서 DP가 적용되기 때문에 참조적 투명성 또한 충족된다.
package apss;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Wildcard_Solution {
public static String W, S;
public static int N;
public static int cache[][];
public static String fileName[];
public static List<String> result;
public static void main(String[] args) throws NumberFormatException, IOException {
// TODO Auto-generated method stub
System.setIn(new FileInputStream("C://eclipse-workspace/sample_input.txt"));
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int T = Integer.parseInt(br.readLine().trim());
for(int testCase=1; testCase<=T; testCase++) {
W = br.readLine().trim();
N = Integer.parseInt(br.readLine().trim());
result = new ArrayList<String>();
fileName = new String[N];
for(int i=0; i<N; i++) {
fileName[i] = br.readLine().trim();
S = fileName[i];
cache = new int[101][101];
for(int k=0; k<101; k++) {
for(int j=0; j<101; j++) {
cache[k][j] = -1;
}
}
bw.flush();
if(dynamicMatch(0,0) == 1) {
result.add(fileName[i]);
}
}
Collections.sort(result);
for(int i=0; i<result.size(); i++) {
bw.write(result.get(i) + "\n");
}
}
bw.close();
}
// 와일드카드 패턴 w가 문자열 s에 대응되는지 여부를 반환한다.
public static boolean match(String w, String s) {
// w[pos]와 s[pos]를 맞춰나간다.
int pos = 0;
while(pos < s.length() && pos<w.length() && (w.charAt(pos) == '?' || w.charAt(pos) == s.charAt(pos))) {
pos++;
}
// 더이상 대응할 수 없으면 왜 while문이 끝났는지 확인한다.
// 2. 패턴 끝에 도달해서 끝난 경우: 문자열도 끝났어야 대응됨
if(pos == w.length()) {
return pos == s.length();
}
// 4. *를 만나서 끝난 경우: *에 몇 글자를 대응해야 할지 재귀 호출하면서 확인한다.
if(w.charAt(pos) == '*') {
for(int skip=0; pos+skip<=s.length(); skip++) {
if(match(w.substring(pos+1), s.substring(pos+skip))) {
return true;
}
}
}
// 이 외의 경우에는 모두 대응되지 않는다.
return false;
}
// O(n^3) : 패턴길이 n, 문자열길이 n, 재귀호출 n
public static int dynamicMatch(int w, int s) {
// -1은 아직 답이 계산되지 않았음을 의미한다.
// 1은 해당 입력들이 서로 대응됨을 의미한다.
// 0은 해당 입력들이 서로 대응되지 않음을 의미한다.
int ret = cache[w][s];
if(ret != -1) {
return cache[w][s];
}
// W[w]와 S[s]를 맞춰나간다.
while(w<W.length() && s<S.length() && (W.charAt(w) == '?' || W.charAt(w) == S.charAt(s))) {
w++;
s++;
}
// 더이상 대응할 수 없으면 왜 while문이 끝났는지 확인한다
// 2. 패턴 끝에 도달해서 끝난 경우: 문자열도 끝났어야 참
if(w == W.length()) {
return cache[w][s] = (s == S.length() ? 1 : 0);
}
// 4. *를 만나서 끝난 경우: *에 몇 글자를 대응해야 할지 재귀 호출하면서 확인한다.
if(W.charAt(w) == '*') {
for(int skip=0; skip+s <= S.length(); ++skip) {
if(dynamicMatch(w+1, s+skip) == 1) {
return cache[w][s] = 1;
}
}
}
// 3. 이 외의 경우에는 모두 대응되지 않는다.
return cache[w][s] = 0;
}
public static int dynamicMatch2(int w, int s) {
// -1은 아직 답이 계산되지 않았음을 의미한다.
// 1은 해당 입력들이 서로 대응됨을 의미한다.
// 0은 해당 입력들이 서로 대응되지 않음을 의미한다.
int ret = cache[w][s];
if(ret != -1) {
return cache[w][s];
}
// W[w]와 S[s]를 맞춰나간다.
while(w<W.length() && s<S.length() && (W.charAt(w) == '?' || W.charAt(w) == S.charAt(s))) {
return cache[w][s] = dynamicMatch2(w+1,s+1);
}
// 더이상 대응할 수 없으면 왜 while문이 끝났는지 확인한다
// 2. 패턴 끝에 도달해서 끝난 경우: 문자열도 끝났어야 참
if(w == W.length()) {
return cache[w][s] = (s == S.length() ? 1 : 0);
}
// 4. *를 만나서 끝난 경우: *에 몇 글자를 대응해야 할지 재귀 호출하면서 확인한다.
if(W.charAt(w) == '*') {
if(dynamicMatch2(w+1,s) == 1 || (s < S.length() && dynamicMatch2(w, s+1) == 1)) {
return cache[w][s] = 1;
}
}
// 3. 이 외의 경우에는 모두 대응되지 않는다.
return cache[w][s] = 0;
}
}
'[개발] 지식 > 알고리즘 문제풀이' 카테고리의 다른 글
[알고스팟] ASYMTILING (비대칭 타일링) (0) | 2019.05.06 |
---|---|
[알고스팟] Quantization (0) | 2019.05.05 |
[문제][알고스팟] 울타리 잘라내기 (0) | 2019.01.28 |
[문제][알고스팟] 쿼드트리 뒤집기 (QUADTREE) (0) | 2018.12.10 |
[문제][알고스팟] BOARDCOVER (게임판 덮기) (0) | 2018.08.22 |
<