Algorithm
패턴매칭
GREEN.1229
2021. 6. 2. 10:48
문제 제시
인자로 문자열 2개가 전달된다.
하나는 패턴을 나타내는 문자열이고 하나는 데이터를 담은 문자열이다.
데이터를 담은 문자열이 패턴과 일치하는지를 판별하는 메서드를 만드시오.
예시
func solution(_ pattern: String, _ data: String) -> Bool {
...
}
solution("abbaa", "dog cat cat dog dog") // true
solution("xyxy", "one two two one") // false
solution("cbac", "dog cat bird dog") // true
문제 해결
func solution(_ pattern:String, _ target: String) -> Bool {
// 패턴과 데이터를 문자열로 변환
let patternChar = pattern.map{$0}
let input = data.split(separator: " ")
// 패턴과 데이터 딕셔너리 생성
var patternDic: [Int: String] = [:]
var dataDic: [Int: String] = [:]
var patternResult: [String] = []
var dataResult: [String] = []
var patternCount: Int = 1
var dataCount: Int = 1
// 패턴 배열 비교하여 딕셔너리 생성
for i in patternChar {
if patternDic.isEmpty {
patternDic.updateValue(String(i), forKey: patternCount)
patternCount += 1
} else {
if !(patternDic.values.contains(String(i))) {
patternDic.updateValue(String(i), forKey: patternCount)
}
}
}
// 데이타 배열 비교하여 딕셔너리 생성
for i in input {
if dataDic.isEmpty {
dataDic.updateValue(String(i), forKey: dataCount)
dataCount += 1
} else {
if !(dataDic.values.contains(String(i))) {
dataDic.updateValue(String(i), forKey: dataCount)
}
}
}
// 패턴 딕셔너리 치환
for i in patternChar {
for j in patternDic {
if String(i) == j.value {
patternResult.append(String(j.key))
}
}
}
// 데이터 딕셔너리 치환
for i in input {
for j in dataDic {
if String(i) == j.value {
dataResult.append(String(j.key))
}
}
}
return patternResult == dataResult
}
처음에는 이렇게 복잡하게 for문을 돌면서 어떻게 어찌저찌 구현을 하였다.
그러나 이렇게 한다면 분명 효율성과 시간초과가 나올것이라고 생각하여 리팩토링 해보았다.
func solution(_ pattern: String, _ target: String) -> Bool {
// 패턴 파라미터의 값 중복 제거 (패턴 종류 파악)
let uniquePattern: String = Array(pattern).reduce("", { (result, next) -> String in
if result.contains(next) {
return result
}
return result + String(next)
})
// 타겟 파라미터 공백 문자열 기준 분리
let targets = target.components(separatedBy: " ")
// 패턴과 값을 담을 딕셔너리 생성
var targetDic: [String: String] = [:]
// 패턴의 초기 위치 값
var patternIndex = 0
// 최종 치환된 문자열 변수
var finalTarget: String = ""
// 타겟 돌면서 치환
for i in targets {
// 딕셔너리에 해당 키 값이 있는지 판별
if let value = targetDic[i] {
finalTarget.append(value)
continue
}
guard patternIndex < uniquePattern.count else {
return false
}
// 치환
let patternValue = uniquePattern[uniquePattern.index(uniquePattern.startIndex, offsetBy: patternIndex)]
targetDic[i] = String(patternValue)
finalTarget.append(patternValue)
patternIndex += 1
}
// 최종 패턴과 치환된 타겟 비교
return pattern == finalTarget
}
고차함수를 적절히 사용하여 조금 더 효율성 있게 구조화해보았다.
딕셔너리를 돌며 해당 키에 대한 벨류 값을 비교해주고 있다면 넘어가며 해당 키 값을 추가하고 없다면 새로운 키값과 벨류를 만들어준다.
이런식으로 조금 더 반복문을 줄이고 효율성을 높여보았다.
사용된 개념
- 문자열 다루기
- 조건 / 반복문
- 딕셔너리
- 문자 치환
문제 뒷담화
해당 문제는 간단하게 생각이 들었는데 막상 구현을 하려니 생각보다 빠르고 쉽게 풀리지 않았다.
강제로 처음 문제 해결 방식처럼 구현은 할 수 있었지만 효율적으로 고차함수를 사용하여 푸는것이 머리가 안돌았다.
딕셔너리는 키 값이 중복될 수 없는 점을 활용하여 타겟을 돌며 딕셔너리에 해당 키에 대한 벨류값이 있는지 파악하고 치환해줌으로 해결하였다.