코디잉

09_문자열 검색 본문

자료구조&알고리즘/이론공부

09_문자열 검색

yong_ღ'ᴗ'ღ 2022. 9. 13. 03:29

① 브루트-포스법

- 선형 검색을 확장한 알고리즘

- 문자열의 처음과 과 패턴(찾을 문자열)의 첫 문자가 겹치도록 놓고, 처음부터 순서대로 일치하는지 검사

  문자가 일치하면 계속해서 패턴과 텍스트의 문자를 검사하고, 

  다른 문자가 나오면 검사를 중단하고 패턴을 한 칸 뒤로 옮겨서 반복

- 검사를 진행한 위치를 기억하지 못하고, 계속 패턴의 처음부터 문자열을 비교하므로 효율이 좋지 않다.

- 텍스트에 패턴이 여러 개 있으면 가장 처음 발견한 곳의 인덱스를 반환한다.

static int bfMatch(String txt, String pat) {
    int txtPointer = 0;
    int patPointer = 0;

    while (txtPointer != txt.length() && patPointer != pat.length()) {
        if (txt.charAt(txtPointer) == pat.charAt(patPointer)) {
            txtPointer++;
            patPointer++;
        } else {
            txtPointer = txtPointer - patPointer + 1;
            patPointer = 0;     // 패턴 포인터는 다시 0으로 초기화
        }
    }
    // 검색 성공
    if (patPointer == pat.length())
        return txtPointer - patPointer;
    // 검색 실패
    return -1;
}

+) String.indexOf()

- int indexOf(String str, int fromIndex): fromIndex 생략 시, 처음부터 검사

- int lastIndexOf(String str, int fromIndex): fromIndex 생략 시, 맨 뒤부터 검사

 → fromIndex를 시작점으로, 맨 뒤에서부터 해당 단어를 찾기 시작함

 

+) UTF-8에서는 영문/숫자/기호는 1바이트, 한글/한자는 3바이트

- 반각(半角)문자: 전각문자의 절반을 폭으로 하는 문자  ex) 영문자, 아스키코드문자

- 전각(全角)문자: 문자 폭이 일반적인 영문자 고정폭의 2배정도 가지는 문자  ex) 한글, 한자

 

 

② KMP법

- Knuth, Morris, Pratt 가 거의 같은 시기에 고안해서 이름 앞 글자 각각 따서 명명

- 브루트-포스법과 다르게 중간 검사 결과를 효율적으로 사용하는 알고리즘 (브루트-포스법을 개선)

  → 텍스트와 패턴의 겹치는 부분을 찾아내어 검사를 다시 시작할 위치를 구함

  → 패턴을 최소의 횟수로 옮겨 알고리즘의 효율을 높임

- 몇 번째 문자부터 다시 검색을 시작할 지에 대한 값을 미리 '표'로 만들어 놓음

  → 패턴 안에서 중복되는 문자의 나열을 먼저 찾음 (KMP법 사용)

→ 브루트-포스법보다 복잡하고, Boyer-Moore법과 성능이 같거나 좋지 않아서 실제 프로그램에서는 거의 사용하지 않음

 

static int kmpMatch(String txt, String pat) {
    int txtPointer = 1;     //--패턴을 텍스트의 한 칸 뒤로 옮겨서부터 비교 시작해야하므로
    int patPointer = 0;
    int[] skip = new int[pat.length() + 1];     // 건너뛰기 표

    // 건너뛰기 표 만듦
    skip[txtPointer] = 0;
    while (txtPointer != pat.length()) {
        if (pat.charAt(txtPointer) == pat.charAt(patPointer))
            skip[++txtPointer] = ++patPointer;
        else if (patPointer == 0)   //-- 문자 다름 + pattern pointer의 위치 0
            skip[++txtPointer] = patPointer;
        else                        //-- 문자 다름 + pattern pointer의 위치 0이 아님
            patPointer = skip[patPointer];
            //-- 다음 시작할 때, 이 위치부터 검사 시작
    }

    // 검색
    txtPointer = patPointer = 0;
    while (txtPointer != txt.length() && patPointer != pat.length()) {
        if (txt.charAt(txtPointer) == pat.charAt(patPointer)) {
            txtPointer++;
            patPointer++;
        } else if (patPointer == 0) {
            txtPointer++;
        } else {
            patPointer = skip[patPointer];
        }
    }

    if (patPointer == pat.length())
        return txtPointer - patPointer;
    return -1;
}

 

③ Boyer-Moore법 (보이어-무어법)

- KMP법보다 효율이 더 우수해서, 문자열 검색에 널리 사용하는 알고리즘

- 패턴의 마지막 문자부터 앞쪽으로 검사를 진행하며, 일치하지 않는 문자가 있으면 미리 준비한 표에 따라 패턴을 

  옮길 크기를 정함

  → 뒤에부터 검사해서 패턴에 들어 있지 않은 문자를 발견하면, 해당 위치까지 건너뛸 수 있음

- 각각의 문자를 만났을 때 패턴을 옮길 크기를 저장할 표를 미리 만들어야 한다.

  → 패턴에 존재할 수 있는 모든 문자의 옮길 크기를 계산하고 저장해야 하기 때문에, 건너뛰기 표의 요소 개수는

     『Character.MAX_VALUE + 1』 이다.

static int bmMatch(String txt, String pat) {
    int txtPointer;
    int patPointer;
    int txtLen = txt.length();  // txt의 문자 개수
    int patLen = pat.length();  // pat의 문자 개수
    int[] skip = new int[Character.MAX_VALUE + 1];  // 건너뛰기 표

    // 건너뛰기 표 만듦
    for (txtPointer = 0; txtPointer < Character.MAX_VALUE; txtPointer++)
        skip[txtPointer] = patLen;  //-- 일단 다 패턴 길이로 초기화
    for (txtPointer = 0; txtPointer < patLen - 1; txtPointer++)
        skip[pat.charAt(txtPointer)] = patLen - txtPointer - 1;
        
	//----------└→ 여기까지 하면, 『txtPointer == patLen - 1』
    // 검색
    while (txtPointer < txtLen) {
        patPointer = patLen - 1;    // pattern의 끝 문자에 주목

        while (txt.charAt(txtPointer) == pat.charAt(patPointer)) {
            if (patPointer == 0) 
                return txtPointer;  // 검색 성공
            txtPointer--;
            patPointer--;
        }
        txtPointer += (skip[txt.charAt(txtPointer)] > patLen - patPointer)
                ? skip[txt.charAt(txtPointer)] : patLen - patPointer;

    }
    return -1;  // 검색 실패
}

'자료구조&알고리즘 > 이론공부' 카테고리의 다른 글

11_해시  (0) 2022.09.13
10_연결 리스트(Linked List)  (0) 2022.09.13
08_집합  (0) 2022.09.13
07_정렬 ① 버블정렬/단순선택정렬/단순삽입정렬  (0) 2022.08.26
06_재귀 알고리즘  (0) 2022.08.26
Comments