반응형

(PYTHON)파이썬 알고리즘 - 문자열 검색(블루투 포스법, KMP법, 보이어 무어법)

 

브루투 포스법

def bf_match(txt: str, pat: str) -> int:
    """브루트 포스법으로 문자열 검색"""
    pt = 0  # txt(텍스트)를 따라가는 커서
    pp = 0  # pat(패턴)를 따라가는 커서

    while pt != len(txt) and pp != len(pat):    # pt와 pp가 문자열 끝자리를 벗어나지 않았다면 실행
        if txt[pt] == pat[pp]:  #텍스트문자열과 패턴용 문자열의 pt, pp위치에 값이 같으면
            pt += 1
            pp += 1
        else:   # 값이 다르면
            pt = pt - pp + 1    # 비교할 시작값(pt)을 배열 한칸 우측으로 이동
            pp = 0  # pp 시작값은 0번으로 변경하여 다시 비교

    return pt - pp if pp == len(pat) else -1    # 만약 비교할 패턴이 다맞아서pp가 pat길이와 같은 값이되면 pt - pp를 리턴, 아니면 -1을 리턴

if __name__ == '__main__':
    s1 = input('텍스트를 입력하세요.: ')  # 텍스트용 문자열
    s2 = input('패턴을 입력하세요.: ')    # 패턴용 문자열

    idx = bf_match(s1, s2)  # 문자열 s1~s2를 브루트 포스법으로 검색

    if idx == -1:
        print('텍스트 안에 패턴이 존재하지 않습니다.')
    else:
        print(f'{(idx + 1)}번째 문자에서 일치합니다.')

 

 

표준라이브러리를 사용한 문자열 검색(find, index)

# 문자열에 포함되어 있는 문자열을 검색(find 계열 함수)

txt = input('문자열 txt: ')  # 문자열 나열
ptn = input('문자열 ptn: ')  # 검색할 문자

c = txt.count(ptn)

if c == 0:                                                  # 포함된 문자가 없음
    print('ptn은 txt에 포함되어 있지 않습니다.')
elif c == 1:                                                # 포함된 문자가 1개만 있는 경우
    print('ptn이 txt에 포함되어 있는 인덱스: ', txt.find(ptn))
else:                                                       # 포함된 문자가 2개 이상 있는 경우
    print('ptn이 txt에 포함되어 있는 맨 앞 인덱스: ', txt.find(ptn))
    print('ptn이 txt에 포함되어 있는 맨 끝 인덱스: ', txt.rfind(ptn))
# 문자열에 포함되어 있는 문자열을 검색(index 계열 함수)

txt = input('문자열 txt: ')
ptn = input('문자열 ptn: ')

c = txt.count(ptn)

if c == 0:                                                  # 포함된 문자가 없음
    print('ptn은 txt에 포함되어 있지 않습니다.')
elif c == 1:                                                # 포함된 문자가 1개만 있는 경우
    print('ptn이 txt에 포함되어 있는 인덱스: ', txt.index(ptn))
else:                                                       # 포함된 문자가 2개 이상 있는 경우
    print('ptn이 txt에 포함되어 있는 맨 앞 인덱스: ', txt.index(ptn))
    print('ptn이 txt에 포함되어 있는 맨 끝 인덱스: ', txt.rindex(ptn))

 

 

KMP법

브루트 포스법은 일치하지 않는 문자를 만나면 이전 단계를 검사했던 결과를 버리고 패턴의  첫 문자부터 다시 검사를 수행하지만, KMP법은 검사했던 결과를 버리지 않고 효율적으로 활용하는 알고리즘입니다.

(건너뛰기 표를 만들어 활용)

def kmp_match(txt: str, pat: str) -> int:
    """KMP법에 의한 문자열 검색"""
    pt = 1  # txt를 따라가는 커서
    pp = 0  # pat를 따라가는 커서
    skip = [0] * (len(pat) + 1)  # 건너뛰기 표 

    # 건너뛰기 표 만들기
    skip[pt] = 0
    while pt != len(pat):   # pt 위치가 패턴길이와 같지 않다면 실행
        if pat[pt] == pat[pp]:
            pt += 1
            pp += 1
            skip[pt] = pp
        elif pp == 0:
            pt += 1
            skip[pt] = pp
        else:
            pp = skip[pp]

    # 검색하기
    pt = pp = 0
    while pt != len(txt) and pp != len(pat):    # pt와 pp가 문자열 끝자리를 벗어나지 않았다면 실행
        if txt[pt] == pat[pp]:  #텍스트문자열과 패턴용 문자열의 pt, pp위치에 값이 같으면
            pt += 1
            pp += 1
        elif pp == 0:   # pp가 0이면 
            pt += 1
        else:
            pp = skip[pp]

    return pt - pp if pp == len(pat) else -1    # 만약 비교할 패턴이 다맞아서pp가 pat길이와 같은 값이되면 pt - pp를 리턴, 아니면 -1을 리턴

if __name__ == '__main__':
    s1 = input('텍스트를 입력하세요.: ')  # 텍스트용 문자열
    s2 = input('패턴을 입력하세요.: ')    # 패턴용 문자열

    idx = kmp_match(s1, s2)  # 문자열 s1~s2를 KMP법으로 검색

    if idx == -1:
        print('텍스트 안에 패턴이 존재하지 않습니다.')
    else:
        print(f'{(idx + 1)}번째 문자에서 일치합니다.')

 

 

보이어 무어법

패턴의 끝 문자에서 시작하여 앞쪽을 향해 검사를 수행합니다. 이 과정에서 일치하지 않는 문자를 발견하면 미리 준비한 표를 바탕으로 패턴이 이동하는 값을 결정합니다.

def bm_match(txt: str, pat: str) -> int:
    """보이어 무어법에 의한 문자열 검색"""
    skip = [None] * 256  # 건너뛰기 표

    # 건너뛰기 표 만들기
    for pt in range(256):
        skip[pt] = len(pat)
    for pt in range(len(pat)):
        skip[ord(pat[pt])] = len(pat) - pt - 1

    # 검색하기
    while pt < len(txt):
        pp = len(pat) - 1
        while txt[pt] == pat[pp]:
            if pp == 0:
                return pt
            pt -= 1
            pp -= 1
        pt += skip[ord(txt[pt])] if skip[ord(txt[pt])] > len(pat) - pp \
              else len(pat) - pp

    return -1

if __name__ == '__main__':
    s1 = input('텍스트를 입력하세요.: ')  # 텍스트 문자열
    s2 = input('패턴을 입력하세요.: ')    # 패턴 문자열

    idx = bm_match(s1, s2)  # 문자열 s1~s2를 KMP법으로 검색

    if idx == -1:
        print('텍스트 안에 패턴이 존재하지 않습니다.')
    else:
        print(f'{(idx + 1)}번째 문자에서 일치합니다.')

 

(PYTHON)파이썬 전체보기

 

'(PYTHON)파이썬' 카테고리의 글 목록

전산 관련 경험을 기록 하는 곳

reddb.tistory.com

 

반응형

반응형

(PYTHON)파이썬 알고리즘 - 병합정렬

 

정렬된 배열 병합하기

정렬된 배열 2개(a, b)를 하나의 배열로(c) 정렬하며 병합

from typing import Sequence, MutableSequence

def merge_sorted_list(a: Sequence, b: Sequence, c: MutableSequence) -> None:
    """정렬을 마친 배열 a와 b를 병합하여 c에 저장"""
    pa, pb, pc = 0, 0, 0                 # 각 배열의 커서위치를 0으로 설정
    na, nb, nc = len(a), len(b), len(c)  # 각 배열의 원소수를 할당

    while pa < na and pb < nb:  
        if a[pa] <= b[pb]:   # pa와 pb를 비교하여 pa가 같거나 작으면 값을 c[pc]에 저장
            c[pc] = a[pa]
            pa += 1     # pa 커서위치 우측으로 한칸 이동
        else:
            c[pc] = b[pb]   # pb가 작으면 값을 c[pc]에 저장
            pb += 1     # pb 커서위치 우측으로 한칸 이동
        pc += 1     # pc 커서위치 우측으로 한칸 이동

    while pa < na:              # pa가 배열의 끝에 도달하지 못했다면 남은 원소를 c에 복사
        c[pc] = a[pa]
        pa += 1
        pc += 1

    while pb < nb:              # pb가 배열의 끝에 도달하지 못했다면 남은 원소를 c에 복사
        c[pc] = b[pb]
        pb += 1
        pc += 1

if __name__ == '__main__':
    a = [2, 4, 6, 8, 11, 13]    # 정렬된 배열 a[]
    b = [1, 2, 3, 4, 9, 16, 21] # 정렬된 배열 b[]
    c = [None] * (len(a) + len(b))  # 배열 a개수 + b개수 만큼 빈배열을 만들어 c에 할당
    print('정렬을 마친 두 배열의 병합을 수행합니다.')

    merge_sorted_list(a, b, c)  # 배열 a와 b를 병합하여 c에 저장하는 함수 호출

    print('배열 a와 b를 병합하여 배열 c에 저장하였습니다.')
    print(f'배열 a: {a}')
    print(f'배열 b: {b}')
    print(f'배열 c: {c}')

 

 

sorted() 함수, heapq모듈의 merge() 함수로 병합 정렬하기

# a, b배열이 정렬을 마친 상태가 아니어도 적용이 가능하다는 장점
# 속도가 빠르지 않다는 단점이 있음

a = [2, 1, 6, 8, 11, 13]
b = [1, 2, 3, 4, 9, 16, 21]
c = list(sorted(a+b))  # 배열 a와 b를 병합하여 c에 저장

print('배열 a와 b를 병합하여 배열 c에 저장하였습니다.')
print(f'배열 a: {a}')
print(f'배열 b: {b}')
print(f'배열 c: {c}')
# 속도를 빠르게 병합

import heapq

a = [2, 1, 6, 8, 11, 13]
b = [1, 2, 3, 4, 9, 16, 21]
c = list(heapq.merge(a, b))  # 배열 a와 b를 병합하여 c에 저장

print('배열 a와 b를 병합하여 배열 c에 저장하였습니다.')
print(f'배열 a: {a}')
print(f'배열 b: {b}')
print(f'배열 c: {c}')

 

 

병합 정렬 알고리즘

from typing import MutableSequence

def merge_sort(a: MutableSequence) -> None:
    """병합 정렬"""

    def _merge_sort(a: MutableSequence, left: int, right: int) -> None:
        """a[left] ~ a[right]를 재귀적으로 병합 정렬"""
        if left < right:    # left가 right보다 작으면 실행 (최종적으로 1개씩으로 쪼개질때까지 아래가 진행)
            center = (left + right) // 2

            _merge_sort(a, left, center)            # 재귀호출. 배열 앞부분을 병합 정렬 호출 (중간 기준으로 좌측 호출 - 1개가 될 때까지)
            _merge_sort(a, center + 1, right)       # 재귀호출. 배열 뒷부분을 병합 정렬 호출 (중간 기준으로 우측 호출 - 1개가 될 때까지)
            # 재귀호출이 끝나면 아래에서 병합을 하고 다시 돌아감

            p = j = 0
            i = k = left

            while i <= center:  # left부터 center까지 반복
                 buff[p] = a[i]     # 버퍼[p]에 a[i]를 할당. 중간을 기준으로 좌측 배열을 버퍼에 할당
                 p += 1     # p는 최종적으로 center+1이 됨
                 i += 1     # i도 최종적으로 center+!이 됨

            while i <= right and j < p: # i가 right보다 작으면서 j가 p보다 작다면
                 if buff[j] <= a[i]:    # 버퍼[j]값(좌측첫번째배열값)이 a[i]값(우측첫번째값) 보다 같거나 작으면
                     a[k] = buff[j]     # a[left]에 버퍼[j] 값 할당
                     j += 1       # j 우측으로 한칸이동
                 else:      # 버퍼[j]가 a[i]보다 크면 
                     a[k] = a[i]    # a[left]에 a[i]값 할당
                     i += 1     # i 우측으로 한칸이동
                 k += 1     # 정렬된 k(left)도 우측으로 한칸 이동

            while j < p:    # 버퍼[p]에는 버퍼의 최종값이 들어있는데 j가 아직 p까지 못갔다면 버퍼에 값이 남아있는 상황임
                a[k] = buff[j]  # a[k]에 버퍼[j]값을 할당
                k += 1  # k 한칸 이동
                j += 1  # j 한칸 이동

    n = len(a)
    buff = [None] * n           # 작업용 배열을 생성
    _merge_sort(a, 0, n - 1)    # 배열 전체를 병합 정렬
    del buff                    # 작업용 배열을 소멸

if __name__ == '__main__':
    print('병합 정렬을 수행합니다')
    num = int(input('원소 수를 입력하세요.: '))
    x = [None] * num    # 원소 수가 num인 배열을 생성

    for i in range(num):
        x[i] = int(input(f'x[{i}]: '))

    merge_sort(x)       # 배열 x를 병합 정렬

    print('오름차순으로 정렬했습니다.')
    for i in range(num):
        print(f'x[{i}] = {x[i]}')

 

(PYTHON)파이썬 전체보기

 

'(PYTHON)파이썬' 카테고리의 글 목록

전산 관련 경험을 기록 하는 곳

reddb.tistory.com

 

반응형