Skip to content

Commit 21e0d62

Browse files
committed
new posting about python-algorithm ch 6-3
1 parent 2456feb commit 21e0d62

9 files changed

Lines changed: 235 additions & 25 deletions

File tree

_posts/2025-12-12-algo-ch6-1.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ $e.g.$ 숫자 35는 3과 5 두 개의 자릿수를 가지고 있고, 이것이
3939

4040
**<u>버킷(bucket)</u>**: 정렬 시 사용되는 추가 메모리로 <mark>기수 정렬에서 버킷은 큐(queue)를 사용한다.</mark>
4141
<br>
42+
<br>
43+
<br>
44+
<br>
4245

4346

4447
## **2. 한 자릿수의 정렬**
@@ -57,6 +60,9 @@ $sol.$
5760
```
5861
<img src="/assets/images/python-algorithm/6-3.jpg" alt="그림 6.3 한 자릿수 정렬">
5962
<br>
63+
<br>
64+
<br>
65+
<br>
6066

6167

6268
## **3. 여러 자리 숫자의 정렬**
@@ -85,6 +91,9 @@ $sol \ 2.$
8591
→ 버킷 <mark>2개</mark>만 있으면 정렬이 가능하다!
8692
</p>
8793
<br>
94+
<br>
95+
<br>
96+
<br>
8897

8998

9099
## **4. 기수 정렬 알고리즘**
@@ -140,7 +149,10 @@ step 3 [9310, 416, 5511, 3603, 9741, 7751, 864, 2903, 2920, 6967]
140149
step 4 [416, 864, 2903, 2920, 3603, 5511, 6967, 7751, 9310, 9741]
141150
Radix sorted list: [416, 864, 2903, 2920, 3603, 5511, 6967, 7751, 9310, 9741]
142151
```
143-
<br>
152+
<br>
153+
<br>
154+
<br>
155+
<br>
144156

145157

146158
## **5. 복잡도 분석**
@@ -174,6 +186,9 @@ $$O(d \times (n+n)) = O(dn)$$
174186

175187
<mark>→ 따라서, 기수 정렬읜 $O(n)$의 공간 복잡도를 갖는다.</mark>
176188
<br>
189+
<br>
190+
<br>
191+
<br>
177192

178193

179194
## **6. 기수 정렬의 단점**

_posts/2025-12-13-algo-ch6-2.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,28 @@ toc: true
1616
<br>
1717

1818

19-
입력의 종류에 따라서 리스트의 각 항목들을 단순히 카운트(count)하는 방법으로 정렬할 수 있는데
19+
입력의 종류에 따라서 리스트의 각 항목들을 단순히 **카운트(count)**하는 방법으로 정렬할 수 있는데
2020
이러한 정렬 기법을 **카운팅 정렬(Counting sort)**이라고 한다.
2121
<br>
2222

23-
24-
리스트를 한 번 스캔하면서 각 항목이 리스트에 몇 번 나타났는지 빈도수를 계산한다.
25-
빈도수가 구해지면 가장 작은 항목부터 순서대로 빈도수 만큼 나열한다.
23+
**&lt; 카운팅 정렬 기본 전략 &gt;**
24+
- 리스트를 한 번 스캔하면서 각 항목이 리스트에 몇 번 나타났는지 빈도수를 계산한다.
25+
- 빈도수가 구해지면 가장 작은 항목부터 순서대로 빈도수 만큼 나열한다.
2626

2727
<img src="/assets/images/python-algorithm/6-5.png" alt="그림 6.5 카운팅 정렬 아이디어">
2828
<br>
29+
<br>
30+
<br>
31+
<br>
2932

3033

3134
## **1. 카운팅 정렬을 적용하기 좋은 입력**
3235
- 키(value)가 가질 수 있는 값이 일정한 개수로 제한되는 경우
3336
- $e.g. \ $ ALGORITHM → AGHILMORT (ASCII code는 모두 0 ~ 255사이의 값을 갖는다.)
3437
<br>
38+
<br>
39+
<br>
40+
<br>
3541

3642

3743
## **2. 카운팅 정렬 알고리즘**
@@ -84,7 +90,10 @@ print("counting_sorted list: ", data)
8490
non-sorted list: [1, 4, 1, 2, 7, 5, 2]
8591
counting_sorted list: [1, 1, 2, 2, 4, 5, 7]
8692
```
87-
<br>
93+
<br>
94+
<br>
95+
<br>
96+
<br>
8897

8998

9099
## **3. 복잡도 분석**

_posts/2025-12-13-algo-ch6-3.md

Lines changed: 0 additions & 19 deletions
This file was deleted.

_posts/2025-12-14-algo-ch6-3.md

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
---
2+
title: "[파이썬 알고리즘] Ch 6-3. 문자열 매칭 (1) (Horspool Algorithm)"
3+
date: 2025-12-14 00:08:00 +0900
4+
categories: [algorithm]
5+
tags: [python, programming, coding, counting, sort, mapping, horspool, shift table, Boyer-Moore, heuristic]
6+
comments: true
7+
math: true
8+
toc: true
9+
#pin: true
10+
#image:
11+
#path: thumbnail.png
12+
#alt: image alternative text
13+
---
14+
15+
참고 도서: 최영규, ⌜파이썬 알고리즘⌟ 생능츨판, 2021
16+
<br>
17+
18+
19+
**문자열 매칭 문제**는 길이가 $n$인 텍스트 속에서 길이가 $m$인 패턴의 위치를 찾는 문제이다.
20+
<br>
21+
22+
23+
**억지 기법(brute force)**을 적용하면 최악의 경우 텍스트의 $n-m+1$개 위치에서 각각 $m$번의 비교를 해야 하므로
24+
시간 복잡도는 $O(nm)$이지만, 평균적으로는 이보다 훨씬 좋은 $O(n+m)$의 성능을 보이는 것으로 알려져 있다.
25+
26+
<img src="/assets/images/python-algorithm/6-7.png" alt="그림 6.6 억지 기법을 이용한 문자열 매칭">
27+
<br>
28+
29+
30+
**&lt; 효율적인 문자열 매칭을 위한 기본 전략 &gt;**
31+
- 패턴을 빠르게 검색할 수 있도록 **전처리(preprocessing)**을 통해 필요 정보를 미리 저장해두고,
32+
- 패턴을 검사할 때 이를 활용한다.
33+
<br>
34+
<br>
35+
<br>
36+
<br>
37+
38+
## **1. 호스풀 알고리즘(Horspool algorithm)**
39+
호스풀 알고리즘(Horspool algorithm)은 문자열 패턴의 마지막 문자를 기준으로 비교를 수행하며, 불일치가 발생하면 해당 문자에 대해 미리 계산된 이동 거리를 이용해 패턴을 이동시키는 **휴리스틱** 기반의 문자열 검색 알고리즘이다.
40+
41+
42+
> **휴리스틱(heuristic)**
43+
> - 모든 경우에 대해 최적의 성능을 보장하지는 않지만, 대부분의 경우에서 꾸준히 좋은 성능을 내는 방법
44+
45+
<br>
46+
47+
48+
호스풀 알고리즘의 기본 전략은 다음과 같다.
49+
<br>
50+
51+
52+
## **2. 전처리(preprocessing)**
53+
- 패턴 불일치가 발생할 경우 이동 거리를 계산하기 위한 **시프트 테이블(shift table)** 생성
54+
<br>
55+
<br>
56+
<br>
57+
<br>
58+
59+
## **3. 문자열 패턴 검사**
60+
- <mark>항상 패턴의 마지막 문자부터 비교 (오른쪽 → 왼쪽)</mark>
61+
- 이 방식은 불일치 발생 시 여러 위치를 한 번에 건너뛸 수 있어서 <u>불필요한 비교를 줄이는 데 효과적이다.</u>
62+
63+
> **&lt; 앞에서 부터 비교 &gt;**
64+
> - 첫 글자가 우연히 일치할 수 있음
65+
> - 이후 비교 과정에서 뒤쪽 문자에서 불일치가 발생할 수 있음
66+
> - 따라서, 여러 글자를 비교한 뒤에 불일치를 판단하게 됨
67+
> <br>
68+
>
69+
> **&lt; 뒤에서 부터 비교 &gt;**
70+
> - 첫 비교에 바로 일치 or 불일치를 판단할 수 있음
71+
> - 불일치 시 해당 문자를 기준으로 이동 거리 계산 가능
72+
73+
<img src="/assets/images/python-algorithm/6-8.png" alt="그림 6.8 패턴 맨 뒤 문자부터 앞으로 비교함">
74+
<br>
75+
76+
77+
다음으로 문자열 패턴 검사 시 발생할 수 있는 상황에 대해 알아보자.
78+
<br>
79+
80+
81+
**&lt; $Case \ 1. \ $ 불일치가 발생한 문자가 패턴에 없는 문자라면? &gt;**
82+
- 문자 M은 패턴(BANANA)에 없는 문자이므로 패턴의 위치를 인덱스 1 ~ 5로 한 자리씩 옮기면서 비교하더라도 절대 일치할 수가 없다.
83+
- 따라서, 패턴의 위치를 패턴의 길이(6)만큼 바로 넘겨 검사를 진행하면 된다.
84+
<img src="/assets/images/python-algorithm/6-9.png" alt="그림 6.9 문자가 패턴에 존재하지 않는 경우">
85+
<br>
86+
87+
88+
**&lt; $Case \ 2. \ $ 불일치가 발생한 문자가 패턴에 있는 문자라면? &gt;**
89+
- 패턴의 맨 뒤 글자인 A는 일치하고, 다음 글자인 N은 텍스트 B와 일치하지 않는다.
90+
- 이러한 경우는 텍스트의 B는 패턴에 존재하는 문자이므로 패턴의 B 위치가 텍스트의 B 위치와 일치하도록 건너뛰면 된다.
91+
<img src="/assets/images/python-algorithm/6-10.png" alt="그림 6.10 불일치가 발생한 문자가 패턴에 있는 문자일 경우">
92+
<br>
93+
<br>
94+
<br>
95+
<br>
96+
97+
## **4. 시프트 테이블 생성**
98+
$e.g.$
99+
- 패턴: `BANANA`
100+
- 패턴 길이: `m = 6`
101+
- 문자 집합: `ASCII (0 ~ 127)`
102+
- 기본 규칙:
103+
- 불일치 발생 시 패턴에 없는 문자 → `이동 거리 = m`
104+
- 불일치 발생 시 패턴에 있는 문자 → `마지막 문자 기준 거리`
105+
<br>
106+
107+
**&lt; 1. 패턴 인덱스 확인 &gt;**
108+
- 마지막 인덱스 = `5`
109+
- 마지막 문자 = `A`
110+
111+
|**인덱스**|0|1|2|3|4|5|
112+
|**문자**|B|A|N|A|N|A|
113+
114+
<br>
115+
116+
**&lt; 2. 이동 거리 계산 &gt;**
117+
118+
$이동 거리 = (m-1)-i$
119+
120+
| **문자** | **인덱스($i$)** | **계산식** | **이동거리** |
121+
| :------------------------------: | :-------------: | :----------------: | :----------------------------------: |
122+
| B | 0 | 5 - 0 | 5 |
123+
| <span style="color:red">A</span> | 1 | 5 - 1 | 4 (<span style="color:red">X</span>) |
124+
| N | 2 | 5 - 2 | 3 |
125+
| <span style="color:red">A</span> | 3 | 5 - 3 | 2 (<span style="color:red">X</span>) |
126+
| N | 4 | 5 - 4 | 1 |
127+
| <span style="color:red">A</span> | 5 | 마지막 문자는 제외 | <span style="color:red">X</span> |
128+
129+
<br>
130+
131+
**&lt; 3. 시프트 테이블 작성 &gt;**
132+
133+
| **문자** | **이동거리** | **설명** |
134+
| :------: | :----------: | :----------------------- |
135+
| A | 2 | 마지막 A 기준 (인덱스 3) |
136+
| N | 1 | 마지막 N 기준 (인덱스 1) |
137+
| B | 5 | 마지막 B 기준 (인덱스 0) |
138+
| 나머지 | 6 | 패턴에 없음 |
139+
140+
<br>
141+
142+
위 과정을 통해 아래의 그림과 같은 시프트 테이블(shift table)이 만들어진다.
143+
<img src="/assets/images/python-algorithm/6-11.png" alt="그림 6.11 시프트 테이블 작성">
144+
<br>
145+
<br>
146+
<br>
147+
<br>
148+
149+
## **5. 소스코드**
150+
```python
151+
# 시프트 테이블 만들기
152+
NO_OF_CHARS = 128 # 아스키 문자 개수
153+
154+
def shift_table(pat):
155+
m = len(pat) # 패턴의 길이
156+
tbl = [m] * NO_OF_CHARS # 시프트 테이블 초기화(기본 이동 거리 = m)
157+
158+
for i in range(m-1): # 패턴의 마지막 문자를 제외한 모든 문자에 대해
159+
tbl[ord(pat[i])] = m-i-1 # 불일치 발생 시 이동할 거리 계산
160+
161+
return tbl
162+
```
163+
```python
164+
# 호스풀 알고리즘
165+
def search_horspool(T, P):
166+
m = len(P) # 패턴 문자열 길이
167+
n = len(T) # 텍스트 문자열 길이
168+
t = shift_table(P) # 패턴 기반으로 시프트 테이블 생성
169+
i = m-1 # 텍스트에서 패턴의 마지막 문자가 맞춰질 위치 인덱스
170+
171+
while(i <= n-1): # 텍스트 범위를 벗어나지 않는 동안 반복
172+
k = 0
173+
174+
while k <= m-1 and P[m-1-k]==T[i-k]: # 패턴의 끝에서 왼쪽으로 이동하며 문자 비교
175+
k += 1 # 문자가 일치하면 다음 문자로 이동
176+
177+
if k == m: # 패턴의 모든 문자가 일치할 경우
178+
return i-m+1 # 패턴이 시작된 텍스트의 인덱스 반환
179+
else:
180+
tc = t[ord(T[i-k])] # 불일치가 발생한 텍스트 문자에 대한 시프트 값 조회
181+
i += max(1, (tc-k)) # 시프트 거리 계산
182+
183+
return -1 # 패턴을 찾지 못한 경우
184+
185+
# 호스풀 알고리즘 테스트
186+
print("패턴의 위치:", search_horspool("APPLEMANGOBANANAGRAPE", "BANANA"))
187+
```
188+
```python
189+
# 출력 결과
190+
패턴의 위치: 10
191+
```
192+
<br>
193+
<br>
194+
<br>
195+
<br>
196+
197+
## **6. 복잡도 분석**
198+
**최악의 경우 (worst case): <mark>$O(mn)$</mark>**
199+
- 8행의 외부 루프는 $n-m$번 반복한다.
200+
- 또한, 하나의 위치에서 패턴을 비교하는데 패턴의 길이($m$) 만큼의 비교가 필요하다.
201+
<br>
202+
203+
**평균의 경우 (average case): <mark>$O(n)$</mark>**
204+
- 무작위 텍스트에 대해서는 거의 $O(n)$의 성능을 나타낸다.
205+
- <u>이는 억지 기법보다 훨씬 효울적이다.</u>
95.9 KB
Loading
62.9 KB
Loading
127 KB
Loading
47.2 KB
Loading
171 KB
Loading

0 commit comments

Comments
 (0)