문제 설명
- 문자열 두개 주어지는데 이걸 2글자씩 짤라라! (특수문자, 공백, 숫자 제외)
- 문자열끼리 비교하면서 유사도를 캐치해라 (교집합 / 합집합) * 66536 출력
- 중복하는 것도 min , max 로 정해진 규칙에 따라서 잘 추적해서 풀어라
문제 접근
- 일단 인덱스, 인덱스+1로 배열을 하나씩 만들면 될 것은 같은데
- 중요한 거는 같은원소가 있을 때 이거를 어떻게 머금고 있을 것이냐? 였다. SET로 해버리면 중복이 사라지다보니..
- 그럼 카운팅을 해서 빼줘야하는데, 카운팅은 딕셔너리로 해야하나?
제출 코드
import Foundation
func containsSpecialCharacter(_ text: String) -> Bool {
let specialCharacterSet = CharacterSet.alphanumerics.inverted // 특수문자 및 공백만 포함
return text.unicodeScalars.contains { specialCharacterSet.contains($0) }
}
func solution(_ str1: String, _ str2: String) -> Int {
var str1 = Array(str1.lowercased())
var str2 = Array(str2.lowercased())
var array_1 = [String]()
var array_2 = [String]()
for i in 0 ..< str1.count - 1 {
if !containsSpecialCharacter(
String(str1[i])) && !containsSpecialCharacter(String(str1[i+1])) // 일단 특수문자 포함안될때만 넣어줄 거
{
let new = String(str1[i])+String(str1[i+1])
array_1.append(String(str1[i])+String(str1[i+1]))
}
}
for i in 0 ..< str2.count - 1 {
if !containsSpecialCharacter(
String(str2[i])) && !containsSpecialCharacter(String(str2[i+1])) // 일단 특수문자 포함안될때만 넣어줄 거
{
let new = String(str2[i])+String(str2[i+1])
array_2.append(String(str2[i])+String(str2[i+1]))
}
}
if array_1.isEmpty || array_2.isEmpty {
return 65536
} // 공집합 처리
var dict_1 = [String: Int]()
var dict_2 = [String: Int]()
for item in array_1 {
dict_1[item, default: 0] += 1
}
for item in array_2 {
dict_2[item, default: 0] += 1
}
// print(dict_1)
// print(dict_2)
// 초기 세팅 완료 ~ 이제 로직 구현
// print(array_1)
// print(array_2)
var union = 0 // 합집합
var intersection = 0 // 교집합
for item in dict_1 {
if let tmp = dict_2[item.key] {
union += max(tmp, item.value)
intersection += min(tmp, item.value)
}
else { // 안겹칠 때
union += item.value
}
}
for item in dict_2 {
if let tmp = dict_1[item.key] {
continue
}
else {
union += item.value
}
}
print(array_1)
print(array_2)
print(union)
print(intersection)
return Int((Float(intersection) / Float(union)) * 65536)
}
// print(solution("FRANCE", "french"))
print(solution("aa1+aa2", "AAAA12"))
// print(solution("handshake", "shake hands"))
// print(solution("E=M*C^2", "e=m*c^2"))
이렇게 해줬더니,
테스트3
입력값 〉 "aa1+aa2", "AAAA12"
기댓값 〉 43690여기서 빠그라졌는데, 자세히 읽어보니까
세상에나 숫자도 안된다더라 그래서 이를 처리해주는 로직을 변경했다.
func containsSpecialCharacter(_ text: String) -> Bool {
let specialCharacterSet = CharacterSet.alphanumerics.inverted // 특수문자 및 공백만 안되게
return text.unicodeScalars.contains { specialCharacterSet.contains($0) }
}
containsSpecialCharacter
함수를func containsInvalidCharacter(_ text: String) -> Bool {
// 허용된 문자 집합: 알파벳만 포함
let allowedCharacterSet = CharacterSet.letters
return text.unicodeScalars.contains { !allowedCharacterSet.contains($0) }
}
이렇게 변경했다. letters
만 되게끔 처리 방식을 변경했다.
수정 코드
import Foundation
func containsSpecialCharacter(_ text: String) -> Bool {
let specialCharacterSet = CharacterSet.alphanumerics.inverted // 특수문자 및 공백만 포함
return text.unicodeScalars.contains { specialCharacterSet.contains($0) }
}
func solution(_ str1: String, _ str2: String) -> Int {
var str1 = Array(str1.lowercased())
var str2 = Array(str2.lowercased())
var array_1 = [String]()
var array_2 = [String]()
for i in 0 ..< str1.count - 1 {
if !containsSpecialCharacter(
String(str1[i])) && !containsSpecialCharacter(String(str1[i+1])) // 일단 특수문자 포함안될때만 넣어줄 거
{
let new = String(str1[i])+String(str1[i+1])
array_1.append(String(str1[i])+String(str1[i+1]))
}
}
for i in 0 ..< str2.count - 1 {
if !containsSpecialCharacter(
String(str2[i])) && !containsSpecialCharacter(String(str2[i+1])) // 일단 특수문자 포함안될때만 넣어줄 거
{
let new = String(str2[i])+String(str2[i+1])
array_2.append(String(str2[i])+String(str2[i+1]))
}
}
if array_1.isEmpty || array_2.isEmpty {
return 65536
} // 공집합 처리
var dict_1 = [String: Int]()
var dict_2 = [String: Int]()
for item in array_1 {
dict_1[item, default: 0] += 1
}
for item in array_2 {
dict_2[item, default: 0] += 1
}
// 초기 세팅 완료 ~ 이제 로직 구현
// print(array_1)
// print(array_2)
var union = 0 // 합집합
var intersection = 0 // 교집합
for item in dict_1 {
if let tmp = dict_2[item.key] {
union += max(tmp, item.value)
intersection += min(tmp, item.value)
}
else { // 안겹칠 때
union += item.value
}
}
for item in dict_2 {
if let tmp = dict_1[item.key] {
continue
}
else {
union += item.value
}
}
return Int((Float(intersection) / Float(union)) * 65536)
}
if array_1.isEmpty || array_2.isEmpty {
return 65536
}
이 부분 때문이었는데, 나는 무조건 최소값이 65536이 나와야한다고 생각을 했는데, 한쪽만 공집합이면 아예 0이 나오는 것이더라. 좀 제대로 읽고 풀걸 그랬다.
if array_1.isEmpty && array_2.isEmpty {
return 65536
}
최종코드
&&
로 수정해줘서 다행히 다 맞을 수는 있었다.import Foundation
func containsInvalidCharacter(_ text: String) -> Bool {
// 허용된 문자 집합: 알파벳만 포함
let allowedCharacterSet = CharacterSet.letters
return text.unicodeScalars.contains { !allowedCharacterSet.contains($0) }
}
func solution(_ str1: String, _ str2: String) -> Int {
var str1 = Array(str1.lowercased())
var str2 = Array(str2.lowercased())
var array_1 = [String]()
var array_2 = [String]()
for i in 0 ..< str1.count - 1 {
if !containsInvalidCharacter(
String(str1[i])) && !containsInvalidCharacter(String(str1[i+1])) // 일단 특수문자 포함안될때만 넣어줄 거
{
let new = String(str1[i])+String(str1[i+1])
array_1.append(String(str1[i])+String(str1[i+1]))
}
}
for i in 0 ..< str2.count - 1 {
if !containsInvalidCharacter(
String(str2[i])) && !containsInvalidCharacter(String(str2[i+1])) // 일단 특수문자 포함안될때만 넣어줄 거
{
let new = String(str2[i])+String(str2[i+1])
array_2.append(String(str2[i])+String(str2[i+1]))
}
}
if array_1.isEmpty && array_2.isEmpty {
return 65536
} // 공집합 처리
var dict_1 = [String: Int]()
var dict_2 = [String: Int]()
for item in array_1 {
dict_1[item, default: 0] += 1
}
for item in array_2 {
dict_2[item, default: 0] += 1
}
// 초기 세팅 완료 ~ 이제 로직 구현
var union = 0 // 합집합
var intersection = 0 // 교집합
for item in dict_1 {
if let tmp = dict_2[item.key] {
union += max(tmp, item.value)
intersection += min(tmp, item.value)
}
else { // 안겹칠 때
union += item.value
}
}
for (key, value) in dict_2 {
if dict_1[key] == nil {
union += value
}
}
if union == 0 {
return 65536
}
return Int((Float(intersection) / Float(union)) * 65536)
}
타인의 코드
import Foundation
extension String {
var isAlphanumeric: Bool {
return !isEmpty && range(of: "[^a-zA-Z]", options: .regularExpression) == nil
}
}
func solution(_ str1:String, _ str2:String) -> Int {
let str1 = str1.lowercased()
let str2 = str2.lowercased()
var aDic: [String:Int] = [:]
var bDic: [String:Int] = [:]
var denomiator: Int = 0
var numerator: Int = 0
for i in 0 ..< str1.count - 1 {
let subString = String(str1.prefix(i+2).suffix(2))
if !subString.isAlphanumeric { continue }
if aDic[subString] != nil {
aDic[subString]! += 1
} else {
aDic[subString] = 1
}
}
for i in 0 ..< str2.count - 1 {
let subString = String(str2.prefix(i+2).suffix(2))
if !subString.isAlphanumeric { continue }
if bDic[subString] != nil {
bDic[subString]! += 1
} else {
bDic[subString] = 1
}
}
if aDic.isEmpty && bDic.isEmpty { return 65536 }
aDic.keys.forEach { key in
numerator += min(aDic[key] ?? 0, bDic[key] ?? 0)
}
Set(aDic.keys.map { String($0) } + bDic.keys.map { String($0) }).forEach { key in
denomiator += max(aDic[key] ?? 0, bDic[key] ?? 0)
}
return 65536 * numerator / denomiator
}
- 고수들은 항상
extension
사용해서 아예 그 타입에 대해서 좀 더 직관적으로 표현하고자 하는 것 같다. 나는 그냥 함수로 만들어줬는데 이런것들을 좀 다음에 사용해야 할듯prefix().suffix()
를 해줌으로서 2개씩 잘라주는 방식을 학습할 수 있었다.- 사실 가장 큰 차이는
containsInvalidCharacter
일 것이다.func containsInvalidCharacter(_ text: String) -> Bool { let allowedCharacterSet = CharacterSet.letters return text.unicodeScalars.contains { !allowedCharacterSet.contains($0) } }
CharacterSet 공식문서
유니코드 범위를 기반으로 문자 그룹을 정의/ 문자열의 특정 문자들이 특정 그룹에 속하는지 검사할 때 사용하라고 적혀있다.
Returns a character set containing the characters in Unicode General Category L and M. 가 무슨 뜻일까?
L*(Letter)
L 카테고리는 "문자(letter)"를 포함하며, 다시 세부적으로 나뉩니다Lu: Uppercase Letter (대
문자, 예: A, B, C)
M* (Mark)
M 카테고리는 부가 기호(mark)를 포함하며, 문자와 결합하여 동작하는 기호
그니까 즉 문자열 관련된 기호는 가능하면서 원래는 문자만 가능하다는 뜻이다. 사실 [^a-zA-Z]
보다는 좀 더 큰 범위였다.
그럼 여기에 이제 N*이 포함된다고 하니, N은 숫자다.
N* (Number): 숫자
유니코드에서 정의된 모든 숫자가 이에 해당한다.
처음에 숫자가 되는지알고 isalphanumerics를 해줬던 것이다 그래서.. 근데 맞기는 했는데 이게 테스트케이스가 요상한걸 던져주지 않아서지, 사실 문제에서 원하는 것은 [^a-zA-Z]
이기를 바라는 것 같아서 정규식을 좀 더 공부해야할 필요성을 느끼는 계기가 되었다.