[Algorithms] String(Pattern) Matching
String ์ด๋
๊ธธ์ด๊ฐ 0 ์ด์์ธ Character ๋ค์ ๋์ด์ ์๋ฏธํฉ๋๋ค.
๊ธธ์ด๊ฐ 0 ์ธ String ์ "" ์ ๊ฐ์ด ํํํ ์ ์์ผ๋ฉฐ, Empty String ์ด๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
๊ทธ์ ๋ฐ๋๋ก, ๊ธธ์ด๊ฐ 1 ์ด์์ธ String ์ Non-empty String ์ด๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
String ์ ์์
- C++ Program : C++ ๋ก ์์ฑํ .cpp ํ์ผ๋ ํ๋์ ๋ฌธ์์ด์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค.
- HTML Document : <html><h3>ํธํธ์!!</h3></html> ๊ณผ ๊ฐ์ด ์ฐ๋ฆฌ๊ฐ ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด์ ์นํ์ด์ง๋ฅผ ์ด์ฉํ ๋, ์ฌ์ฉ๋๋ HTML ํ์ผ๋ ํ๋์ ๋ฌธ์์ด์
๋๋ค.
- DNA Sequence : {A, C, G, T} ๋ก ๊ตฌ์ฑ๋ ์ผ๊ธฐ์์ด์ ๋์ด๋ ๋ฌธ์์ด์
๋๋ค.
- Digitized Image : ๋์งํธ ์ด๋ฏธ์ง๋ {0, 1} ๋ก ๊ตฌ์ฑ๋ ๋ฐ์ด๋๋ฆฌ ๋ฌธ์์ด์
๋๋ค.
Substring
๋ฌธ์์ด P ์ Substring ์, P[i, j] ์ ๊ฐ์ด ํํํ ์ ์์ต๋๋ค. i์ j๋ Index ๋ฅผ ์๋ฏธํฉ๋๋ค.
์ฆ ์ ์ฒด ๋ฌธ์์ด P ์ ์ผ๋ถ๋ถ์ ์๋ฏธํ๋ฉฐ, ๊ทธ ์ผ๋ถ๋ถ์ด P ์ ํฌํจ๋์ด์ผ ํฉ๋๋ค.
Prefix of P : P ์ ์ ๋์ฌ๋ฅผ ์๋ฏธํ๋ฉฐ, P[i, j] ์์ i ๊ฐ 0์ผ๋ก ๊ณ ์ ๋ ๊ฒฝ์ฐ๋ฅผ ์๋ฏธํฉ๋๋ค. P ์ ์๋ถ๋ถ์ ์๋ผ์จ ํํ๋ก ์๊ฐํ๋ฉด ๋ฉ๋๋ค. P์ ๊ธธ์ด๊ฐ m ์ด๋ผ๊ณ ํ ๋, P ์ ์ ๋์ฌ๋ ์ด m ๊ฐ ์กด์ฌํ ์ ์์ต๋๋ค.
Suffix of P : P ์ ์ ๋ฏธ์ฌ๋ฅผ ์๋ฏธํ๋ฉฐ, P[i, j] ์์ j๊ฐ m-1 ๋ก ๊ณ ์ ๋ ๊ฒฝ์ฐ๋ฅผ ์๋ฏธํฉ๋๋ค. P ์ ๋ท๋ถ๋ถ์ ์๋ผ์จ ํํ๋ก ์๊ฐํ๋ฉด ๋ฉ๋๋ค.์ ๋ฏธ์ฌ ๋ํ ์ด m ๊ฐ ์กด์ฌํ ์ ์์ต๋๋ค.
Proper Prefix/Suffix of P : ์์ Proper ์ด ๋ถ์ผ๋ฉด ์ง์ ๋์ฌ/์ง์ ๋ฏธ์ฌ ๋ผ๊ณ ํด์๋๋ฉฐ, ์ด๋ ์ ์ฒด ๋ฌธ์์ด P ๋ฅผ ์ ์ธํ ๊ฒฝ์ฐ๋ค์ ๊ฐ๋ฆฌํค๊ฒ ๋ฉ๋๋ค. P[0, m-2] ๊น์ง๋ง Proper Prefix, P[1, m-1] ๊น์ง๋ง Proper Suffix ์ธ ๊ฒ์ ๋๋ค. ์ฌ๊ธฐ์ ์ ์ฒด ๋ฌธ์์ด P์ ๊ฐ์ P[0, m-1] ์ด ํฌํจ๋๋ฉด ์ด๋ Proper Prefix/Suffix ๋ผ๊ณ ํ ์ ์์ต๋๋ค. ์ ์ฒด ๋ฌธ์์ด P ์ธ ๊ฒฝ์ฐ ํ ๊ฐ์ง๊ฐ ๋น ์ก๊ธฐ์, ๊ฐ๊ฐ m-1 ๊ฐ๊ฐ ์กด์ฌํ๊ฒ ๋ฉ๋๋ค.
Pattern Matching Problem ์ Text T ์ Pattern P ๊ฐ ์ฃผ์ด์ก์ ๋, T์ Substring ์ค P์ ๊ฐ์ ๊ฒ์ด ์๋์ง๋ฅผ ํ์ธํ๋ ๋ฌธ์ ์ ๋๋ค.
Brute-Force Algorithm
๋ชจ๋ ๊ฐ๋ฅํ ๊ฒฝ์ฐ๋ฅผ ์ฒดํฌํ๋ ๋ฐฉ์์ ์๊ณ ๋ฆฌ์ฆ์ผ๋ก, Character-by-Character Comparison ์ ํฉ๋๋ค.
Mismatch ๊ฐ ๋ฐ์ํ์ ๋, ๋ฌด์กฐ๊ฑด ํ ์นธ์ฉ๋ง ์ค๋ฅธ์ชฝ์ผ๋ก ์์ง์ด๊ณ ๋ค์ ์ฒ์๋ถํฐ ๋น๊ต๋ฅผ ํ๋ ๋ฐฉ์์
๋๋ค.
Brute-Force Algorithm ์ C++ ์ฝ๋๋ก ๋ํ๋ด๋ฉด ์๋์ ๊ฐ์ต๋๋ค.
#include <iostream>
#include <string>
using namespace std;
int BruteForceMatch(string T, string P) {
/*
T : ๊ธธ์ด๊ฐ n ์ธ Text
P : ๊ธธ์ด๊ฐ m์ธ Pattern
Input Size : n + m
*/
int n = T.length(), m = P.length();
for (int i=0; i<=n-m; i++) { // i ๋ Text Index
int j = 0; // Pattern index
while (j < m && T[i + j] == P[j])
j ++;
if (j == m) // Pattern Matching!
return i; // T[i] ๋ถํฐ Pattern ๋ฐ์
}
return -1; // No Match
}
Worst Case
T = aaaaaaaaaaaaah
P = aaah
์
๋ ฅ์ผ๋ก ๋ค์ด์จ ๋ ๊ฐ์ ๋ฌธ์์ด์ด ์์ ๊ฐ์ ๋, ๋ฐ๊นฅ ๋ฐ๋ณต๋ฌธ(i ๋ฐ๋ณต๋ฌธ) ํ ๋ฒ์ ์ฌ์ดํด๋น ์ด m ๋ฒ์ ๋น๊ต๋ฅผ ์ํํด์ผ ํฉ๋๋ค. ์ด ์ฌ์ดํด์ด n-m ๋ฒ์ด๋ฏ๋ก, O(m(n-m)) => O(nm) time ์ ์ํ๋ฉ๋๋ค.
KMP Algorithm
ํ๋ค์์ Knuth-Morris-Pratt's Algorithm ์ ๋๋ค. ์ด ์๊ณ ๋ฆฌ์ฆ์ Brute-Force Algorithm ์์ ๋ฐ์ํ๋ ์ค๋ณต๋๋ ๋น๊ต ์ฐ์ฐ์ ์ค์ด๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ ์๊ณ ๋ฆฌ์ฆ์ ๋๋ค. ์ด๋ฅผ ์ํด์ ํ ์นธ์ฉ๋ง Shift ํ๋ Brute-Force Algorithm ๊ณผ ๋ค๋ฅด๊ฒ, ์ฌ๋ฌ ์นธ์ Shift ํฉ๋๋ค.
Mismatch ๊ฐ ๋ฐ์ํ์ ๋, ์ค๋ณต ๋น๊ต ์ฐ์ฐ์ด ๋ฐ์ํ์ง ์์ผ๋ฉฐ ์ฌ๋ฐ๋ฅธ Pattern Matching ๊ฒฐ๊ณผ๋ฅผ ๋ผ ์ ์๋ ์ต๋ Shift ๋ฅผ ์งํํฉ๋๋ค.
KMP ์๊ณ ๋ฆฌ์ฆ์ ํจํด์ Preprocessing(์ ์ฒ๋ฆฌ) ํ๋ฉฐ, Mismatch ๋ฐ์ ์ ๋ช ์นธ์ Shift ํ ์ ์๋์ง๋ฅผ ๋ฏธ๋ฆฌ ๊ณ์ฐํด๋์ต๋๋ค. ์ด๋ฌํ ์์ ์ ์ํํ๋ ํจ์๋ฅผ failureFunction F(j) ๋ผ๊ณ ํฉ๋๋ค. ์ด ํจ์๋, P[0, j] ์ Prefix ์ P[1, j] ์ Suffix ๊ฐ ๊ฐ์ ๊ฒ๋ค ์ค ๊ฐ์ฅ ํฐ ์ฌ์ด์ฆ๋ก ์ ์๋ฉ๋๋ค.
์ด๋ ๊ฒ mutiple shift ๋ฅผ ํด๋ ๊ด์ฐฎ์ ์ด์ (Correctness) ๋, Longest ๋ก ์ฐพ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ๋ง์ฝ shift ํด์ ๋์ด๊ฐ ์์ญ์ Pattern ์ด ๋ฐ์ํ ์ ์๋ค๋ฉด, failureFunction ์์ ๋ Largest Size ๋ฅผ ์ฐพ์์ด์ผ ํฉ๋๋ค.
KMP Algorithm ์ C++ ๋ก ๊ตฌํํ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
#include <iostream>
#include <string>
using namespace std;
int *failureFunction(string P)
{
/*
Pattern Preprocessing Function
O(2m) time => O(m) time
*/
int m = P.length(), i = 1, j = 0; // F[0] ์ ์ด๋ฏธ 0์ผ๋ก ์ด๊ธฐํ๋ฅผ ํ๊ธฐ์, i=1 ๋ถํฐ ์์
int *F = new int[m + 1];
F[0] = 0; // index 0 ์ ํญ์ 0์ผ๋ก ์ด๊ธฐํ
while (i < m) // ๋ฐ๋ณต ํ์ 2m ๋ฒ์ ๋๊ธธ ์ ์์
{
if (P[i] == P[j]) // j+1 ๊ฐ์ ๋ฌธ์๊ฐ ์ผ์นํ๋ค!! ๋ง์์ผ m ๋ฒ ์ํ๋๋ค.
{
F[i] = j + 1; // update
i++;
j++; // ๋ Longest ๊ฐ ์๋ ์ฒดํฌ
}
else if (j > 0) // ํ๋ ์ด์์ด๋ผ๋ ๋ง์ท๋ค.. mismatch ๊ฐ ๋ฐ์ํ ์ํฉ
j = F[j - 1]; // Use Failure Function to Shift P
else // j == 0. mismatch ๊ฐ ๋ฐ์ํ ์ํฉ. ๋ง์์ผ m ๋ฒ
{
F[i] = 0; // No Match
i++;
}
}
return F;
}
int KMPMatch(string T, string P)
{
/*
O(m+n) time. <= O(m) time ์ failureFunction ์ํ ์๊ฐ
*/
int *F = failureFunction(P); // Pattern Preprocessing => O(m) time
int i = 0; // ํ์ฌ ๋น๊ตํ๊ณ ์๋ T์ Index. ๋น๊ต ์์ ์ง์ ์ด ์๋๋ค.
int j = 0; // ํ์ฌ ๋น๊ตํ๊ณ ์๋ P์ Index
int n = T.length(), m = P.length();
while (i < n) // O(n) time.
{
// Match
if (T[i] == P[j]) // ์ง๊ธ ๋ณด๊ณ ์๋ ๋ฌธ์๋ผ๋ฆฌ๋ ๋ง๊ตฌ๋ง
{
if (j == m - 1) // Pattern ๋ค ๋ง์๋ค.
return i - j; // i ๋ ํ์ฌ T ์ ์กด์ฌํ๋ Pattern ์ ๋ง์ง๋ง ์ธ๋ฑ์ค. j ๋ ํ์ฌ P์ ๋ง์ง๋ง ์ธ๋ฑ์ค. ์ด ๋์ ๋นผ๋ฉด T ์ ๋ฑ์ฅํ๋ Pattern ์ ์ฒซ ์ธ๋ฑ์ค๋ฅผ ๊ตฌํ ์ ์๋ค.
i++; // ๋ค์๊บผ๋ ๋น๊ตํด๋ณด์
j++; // ๋ค์๊บผ๋ ๋น๊ตํด๋ณด์
}
// MisMatch
else if (j > 0) // ํ๋๋ผ๋ ๋ง์๋ค..
j = F[j - 1]; // i ์ฆ๊ฐ X
else
i++; j=0;
}
return -1; // No Match
}
Boyer-Moore Algorithm Variation Version
๋ณด์ด์ด ๋ฌด์ด ์๊ณ ๋ฆฌ์ฆ์์๋ Heuristic ์ด๋ผ๋ ์๋ก์ด ๊ฐ๋ ์ด ๋ฑ์ฅํฉ๋๋ค. ํด๋ฆฌ์คํฑ์ด๋, ๊ฒฝํ์ ๊ธฐ๋ฐํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐ, ํ์ต, ๋ฐ๊ฒฌํด๋ด๋ ๋ฐฉ๋ฒ์ ์๋ฏธํฉ๋๋ค. Global Optimal Solution ์ ์ฐพ๋ ๊ฒ์ด ๋ชฉํ์ด์ง๋ง, ๊ฐ๋ ์ค์ฐจ๊ฐ ๋ฐ์ํ ์๋ ์๋ ์๊ณ ๋ฆฌ์ฆ์ ๋๋ค. ๋ณด์ด์ด ๋ฌด์ด ์๊ณ ๋ฆฌ์ฆ์๋ 2๊ฐ์ ํด๋ฆฌ์คํฑ์ด ํฌํจ๋ฉ๋๋ค.
1. Looking-glass Heuristic
Looking-glass ๋ ๊ฑฐ๊พธ๋ก์, ๋ฐ๋์ ๋ผ๋ ์๋ฏธ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๋ณด์ด์ด ๋ฌด์ด ์๊ณ ๋ฆฌ์ฆ์์๋, Pattern P๋ฅผ T ์ ์ผ๋ถ๋ถ์ ๋น๊ตํ ๋ ๋ค์์๋ถํฐ ๋น๊ตํฉ๋๋ค.
2. Character-jump Heuristic
Text ์ index i ์์ Mismatch ๊ฐ ๋ฐ์ํ์ ๋, T[i] = c ๋ผ๊ณ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค.
๋ง์ฝ P ์ c ๋ฌธ์๊ฐ ์๋ค๋ฉด, P ์์ ๊ฐ์ฅ ๋ง์ง๋ง์ ์์นํ c ์ T[i] ๋ฅผ Align ํ๊ณ ๋ค์ P์ ๊ฐ์ฅ ๋ค์์๋ถํฐ ๋น๊ต๋ฅผ ์์ํฉ๋๋ค. P ์ c ๋ฌธ์๊ฐ ์๋ค๋ฉด, P[0] ๊ณผ T[i+1] ์ด ์ ๋ ฌ๋๋๋ก ํฉ๋๋ค. ์ด๋ ๋ณด์ด์ด ๋ฌด์ด ์๊ณ ๋ฆฌ์ฆ์์ ๊ฐ์ฅ ๋ง์ด Shift ํ๋ ์ผ์ด์ค๊ฐ ๋ฉ๋๋ค.
๋๋ต.. ๋ณด์ด์ด ๋ฌด์ด ์๊ณ ๋ฆฌ์ฆ ํ๋ก์ฐ๋ฅผ ๊ทธ๋ ค๋ณด๋ฉด ์ (๋ฐ๋ก ๊ทธ๋ฆฐ)๊ทธ๋ฆผ๊ณผ ๊ฐ์ต๋๋ค..
๋ณด์ด์ด ๋ฌด์ด ์๊ณ ๋ฆฌ์ฆ์ KMP ์๊ณ ๋ฆฌ์ฆ๊ณผ ๋น์ทํ๊ฒ, ๋ณธ๊ฒฉ์ ์ธ Pattern Matching ์ ์ Pattern Preprocessing ์ด ํ์ํฉ๋๋ค. ์ด ์ ์ฒ๋ฆฌ์์๋ Pattern ์์ ํน์ ๋ฌธ์์ ๊ฐ์ฅ ๋ง์ง๋ง ์์น๋ฅผ ์ฐพ๋ ์์ ์ด ์ํ๋ฉ๋๋ค. ํจ์ ์ด๋ฆ์ Last-Occurence Function ์ ๋๋ค. ํน์ ์ํ๋ฒณ c ์ ๋ํด์ P[i] = c ์ธ ๊ฐ์ฅ ํฐ i ๊ฐ์ ์ ์ฅํ๊ฑฐ๋, ๋ง์ฝ Pattern ์ c ๊ฐ ์กด์ฌํ์ง ์๋ค๋ฉด -1 ์ ์ ์ฅํ ๋ฐฐ์ด์ ์์ฑํ๋ ํจ์์ ๋๋ค. ์ด ํจ์๊ฐ ๊ฐ์ฅ ๋จผ์ ํ๋ ์์ ์ Pattern ์ ํฌํจ๋ ์ ์๋ ์ํ๋ฒณ์ ์(s) ๋งํผ์ ๋ฐฐ์ด์ ๋ชจ๋ -1๋ก ์ด๊ธฐํํ๋ ๊ฒ์ ๋๋ค. ์ฌ๊ธฐ์ O(s) time ์ด ํ์ํฉ๋๋ค. ๊ทธ ํ์ Pattern ์ ์์์๋ถํฐ ์ค์บํด๊ฐ๋ฉฐ, ๋ฑ์ฅํ๋ ์ํ๋ฒณ๋ค์ ๊ฐ์ฅ ๋ง์ง๋ง ์์น๋ฅผ ์ ๋ฐ์ดํธ ํ๋ ๋ฐฉ์์ผ๋ก ์ํ๋ฉ๋๋ค. ์ฌ๊ธฐ์๋ O(m) time ์ด ํ์ํฉ๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก ์ด Last-Occurence Function ์ ์ํ์๊ฐ์ O(m + s) time ์ ๋๋ค.
๋ณด์ด์ด ๋ฌด์ด ์๊ณ ๋ฆฌ์ฆ์ C++ ์ฝ๋์ ๋๋ค.
// Last-Occurence Function ๊ตฌํ X
#include "bits/stdc++.h"
using namespace std;
int BoyerMooreMatch(string T, string P) {
int *L = lastOccurenceFunction(P); // O(m+s) time
int n = T.length();
int m = P.length();
int i=m-1, j=m-1;
do {
if (T[i] == P[j]) {
if (j == 0) // Pattern ์ ์๊น์ง ๋น๊ต ์๋ฃ => Match!!
return i; // T์์ P ๊ฐ ๋ฑ์ฅํ๋ ๋งจ ์ index ๋ฆฌํด
else {
// ์ด์ ๊ทธ ์์๊บผ ๋น๊ตํด๋ณด์!
i = i-1;
j = j-1;
}
}
else {
// Character Jump
int l = L[T[i]]; // Mismatch ๊ฐ ๋ฐ์ํ Text ์ Character ๊ฐ Pattern ์์ ๊ฐ์ฅ ๋ง์ง๋ง์ ๋ฑ์ฅํ๋ ์์น๋ฅผ ๊ฐ์ ธ์!
i = i + m - min(j, 1 + l); //
j = m - 1; // ๋ค์ Pattern ์ ๋งจ ๋ค๋ถํฐ ๋น๊ต๋ฅผ ์์
}
} while (i >= n-1)
return -1; // No match
}
Analysis
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[Algorithms] Dynamic Programming
[Algorithms] Dynamic Programming
2023.05.26 -
[C++] ๋ฐฑ์ค BOJ 21939 ๋ฌธ์ ์ถ์ฒ ์์คํ Version 1
[C++] ๋ฐฑ์ค BOJ 21939 ๋ฌธ์ ์ถ์ฒ ์์คํ Version 1
2021.11.18 -
[C++] ๊ฑฐ๋ฆฌ๋๊ธฐ ํ์ธํ๊ธฐ(2021 ์นด์นด์ค ์ฑ์ฉ์ฐ๊ณํ ์ธํด์ญ ์ฝ๋ฉํ ์คํธ)
[C++] ๊ฑฐ๋ฆฌ๋๊ธฐ ํ์ธํ๊ธฐ(2021 ์นด์นด์ค ์ฑ์ฉ์ฐ๊ณํ ์ธํด์ญ ์ฝ๋ฉํ ์คํธ)
2021.11.03 -
[Python] ์ซ์ ๋ฌธ์์ด๊ณผ ์๋จ์ด(2021 ์นด์นด์ค ์ฑ์ฉ์ฐ๊ณํ ์ธํด์ญ ์ฝ๋ฉํ ์คํธ)
[Python] ์ซ์ ๋ฌธ์์ด๊ณผ ์๋จ์ด(2021 ์นด์นด์ค ์ฑ์ฉ์ฐ๊ณํ ์ธํด์ญ ์ฝ๋ฉํ ์คํธ)
2021.11.03