Take action every day.

kmp

by 오민
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
int nfind(int start, int lasts, int lastp, int endmatch, char *string, char *pat);
#define max_string_size 100
#define max_pattern_size 100
#define max_text_size 10000
int pmatch(int i, char *string, char *pat);
void fail(char *pat);
int failure[max_pattern_size];

int main()
{
	char text_array[max_text_size] = {0};
	char search[max_pattern_size] = {0};
	int kor = 0;
	char fname[30] = {0};
	printf("text를 담은 파일명을 입력하시오:");
	scanf("%s", fname);
	FILE *f;
	if ((f = fopen(fname, "r+")) == NULL)
	{
		if ((f = fopen(fname, "w+")) == NULL)
		{
			printf("파일이 열리지 않습니다\n");
			exit(1);
		}
	}
	fread(text_array, 10000, 1, f);
	puts(text_array);
	printf("탐색할 단어를 입력하세요:");
	scanf("%s", search);

	int start = 0;
	int lasts = strlen(text_array) - 1; //string 배열의 마지막 문자 인덱스
	int lastp = strlen(search) - 1;		//pat 배열의 마지막 문자 인덱스
	int endmatch = lastp;
	int i, num = 0;

	while (1)
	{
		if (nfind(start, lasts, lastp, endmatch, text_array, search) == -1)
			break;
		else
		{
			num = nfind(start, lasts, lastp, endmatch, text_array, search);
			printf("%d ", num);
			for (i = num; i < num + 40 + kor; i++)
			{
				if (text_array[i] & 0x80)
				{
					printf("%c%c", text_array[i], text_array[i + 1]);
					kor++;
					i++;
				}
				else if (!(text_array[i] & 0x80))
				{
					printf("%c", text_array[i]);
				}
			}
			kor = 0;
		}
		printf("\n");
		start = num + 1;
		endmatch = num + lastp + 1;
	}

	fclose(f);
	printf("\nKMP 방법\n");
	int cnt = 0;
	fail(search);
	while (1)
	{
		if (pmatch(cnt, text_array, search) == -1)
		{
			break;
		}
		else
		{
			num = pmatch(cnt, text_array, search);
			printf("%d ", num);
			for (i = num; i < num + 40 + kor; i++)
			{
				if (text_array[i] & 0x80)
				{
					printf("%c%c", text_array[i], text_array[i + 1]);
					kor++;
					i++;
				}
				else if (!(text_array[i] & 0x80))
				{
					printf("%c", text_array[i]);
				}
			}
			kor = 0;
			printf("\n");
			cnt = num + 1;
		}
	}
	return 0;
}

int nfind(int start, int lasts, int lastp, int endmatch, char *string, char *pat)
{ //비효율적인 방법
	int i, j;
	for (i = 0; endmatch <= lasts; endmatch++, start++) //endmatch가 lasts까지 올때까지
	{
		if (string[endmatch] == pat[lastp])
		{
			for (j = 0, i = start; j < lastp && string[i] == pat[j]; i++, j++) //j가 lastp보다 작고 string[i]와 pat[j]가 같으면 i++,j++
				if (j == lastp - 1)											   //마지막까지 검사, j가 lastp까지 오면 매칭 성공
					return start;
		}
	}
	return -1;
}

void fail(char *pat)
{ //가장 중요한 부분.
	int i, j;
	int n = strlen(pat);
	failure[0] = -1; //시작지점은 -1

	for (j = 1; j < n; j++)
	{
		// decide failure[j]; failure[j]번째 칸의 값을 구한다. (어디까지 최대로 같은가)
		i = failure[j - 1];						   //i에는 이전 칸의 인덱스값이 들어간다. 즉, 바로 앞은 i 까지 같았다.
		while ((i >= 0) && (pat[i + 1] != pat[j])) //i가 0 이상인데 이번칸의 정보가 i 다음칸과 다른 상태라면
			i = failure[i];						   //i에는 i번째에 든 인덱스값이 들어간다.(반복)
		if (pat[i + 1] == pat[j])				   //그러고는 바뀐 i의 다음 칸과 현재 칸의 정보가 같으면
			failure[j] = i + 1;					   //값을 1 늘인다.
		else
			failure[j] = -1; //다 해당되지 않으면 -1을 넣는다.
	}
}

int pmatch(int i, char *string, char *pat)
{ /* Knuth, Morris, Pratt string matching algorithm */
	int j = 0;
	int lens = strlen(string);
	int lenp = strlen(pat);
	while (i < lens && j < lenp)
	{
		if (string[i] == pat[j]) //일치하면 한칸씩 다음 문자 탐색
		{
			i++;
			j++;
		}
		else if (j == 0)
			i++; //일치하지 않고 탐색하는 부분이 아직 패턴의 시작점이라면 다음 문자로.
		else	 //일치하지는 않는데 탐색하는 부분이 패턴의 시작점이 아니라면
			j = failure[j - 1] + 1;
	}
	if (j == lenp)
		return (i - lenp); //일치하는 패턴을 찾았다면 문자열에서 패턴의 시작지점을 반환하자.
	else
		return -1; //못찾았다면 -1을 반환하자.
}

이상하게도 원래 되던 한글입력에 대해서는 작동을 안하지만, 해당 문제는 kmp에 대한 기능공부가 아니기때문에 넘어갔다.

나중에 여유로울때 다시 보도록 하자.

'Language > C' 카테고리의 다른 글

승자 트리  (0) 2021.03.29
Heap  (0) 2021.03.29
포인터  (0) 2021.03.29
BST  (0) 2021.03.28
파일처리  (0) 2021.03.17

블로그의 정보

OMIN

오민

활동하기