Hello, Freakin world!

[백준 1786][KMP] 찾기 - while, 재귀버전 본문

알고리즘/PS

[백준 1786][KMP] 찾기 - while, 재귀버전

johnna_endure 2020. 8. 5. 20:52

 

https://www.acmicpc.net/problem/1786

 

1786번: 찾기

첫째 줄에, T 중간에 P가 몇 번 나타나는지를 나타내는 음이 아닌 정수를 출력한다. 둘째 줄에는 P가 나타나는 위치를 차례대로 출력한다. 예컨대, T의 i~i+m-1번 문자와 P의 1~m번 문자가 차례로 �

www.acmicpc.net

 

while문 버전

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/*
KMP 알고리즘
문제 이름 : 찾기
 */
public class Main {
	public static void main(String[] args) throws IOException {
		InputReader inputReader = new InputReader();
//		InputReader inputReader = new InputReader();
		String text = inputReader.readLine();
		String pattern = inputReader.readLine();

		KMPTextReader reader = new KMPTextReader(text, pattern);
		List<Integer> ret = reader.search(text, pattern);

		System.out.println(ret.size());
		if(ret.size() == 0) return;
		StringBuilder sb = new StringBuilder();
		ret.stream().forEach(i -> sb.append((i+1)+"\n"));
		sb.deleteCharAt(sb.length()-1);
		System.out.println(sb.toString());
	}
}

class KMPTextReader {
	private String text;
	private String pattern;
	private int[] pi;

	public KMPTextReader(String text, String pattern) {
		this.text = text;
		this.pattern = pattern;
	}
	//text의 부분 문자열로 pattern이 출현하는 시작 위치들을 모두 반환한다.
	public List<Integer> search(String text, String pattern) {
		int textLength = text.length(), patternLength = pattern.length();
		List<Integer> ret = new ArrayList<>();

		pi = getPartialMatch(pattern);

		int begin = 0, matched = 0;
		while(begin + patternLength <= textLength) {
			//만약 짚더미의 해당 글자가 바늘의 해당 글자와 같다면
			if(matched < patternLength && text.charAt(begin+matched) == pattern.charAt(matched)) {
				matched++;
				//결과적으로 m글자가 모두 일치했다면 답에 추가한다.
				if(matched == patternLength) ret.add(begin);
			}
			else {
				//예외 : matched가 0인 경우에는 다음 칸에서부터 계속
				if(matched == 0) begin++;
				else {
					begin += matched - pi[matched-1];
					//begin을 옮겼다고 처음부터 다시 비교할 필요가 없다.
					//옮긴 후에도 pi[matched-1]만큼은 항상 일치하기 때문이다.
					matched = pi[matched-1];
				}
			}
		}
		return ret;
	}

	public int[] getPartialMatch(String pattern) {
		pi = new int[pattern.length()];

		int begin = 1, matched = 0;

		//비교할 문자가 N의 끝에 도달할 때까지 찾으면서 부분 일치를 모두 기록한다.
		while(begin + matched < pattern.length()) {
			if(pattern.charAt(begin+matched) == pattern.charAt(matched)) {
				matched++;
				pi[begin+matched-1] = matched;
			} else {
				if(matched == 0) begin++;
				else {
					begin += matched - pi[matched-1];
					matched = pi[matched-1];
				}
			}
		}
		return pi;
	}
}

class InputReader {
	private BufferedReader br;

	public InputReader() {
		br = new BufferedReader(new InputStreamReader(System.in));
	}

	public InputReader(String filepath) {
		try {
			br = new BufferedReader(new FileReader(filepath));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public List<Character> readLineIntoCharList() throws IOException {
		List<Character> l = new ArrayList<>();
		while(true) {
			int readVal = br.read();
			if(readVal == '\n' || readVal == -1) break;
			l.add((char)readVal);
		}
		return l;
	}

	public boolean ready() throws IOException {
		return br.ready();
	}

	public String readLine() throws IOException {
		return br.readLine();
	}
	public int readInt() throws IOException {
		return Integer.parseInt(readLine());
	}
	public Long readLong() throws IOException {
		return Long.parseLong(readLine());
	}
}

 

 

 

 

 

 

아래는 재귀 버전

 


import java.util.ArrayList;
import java.util.List;

/*
pi : 문자열에서 집두사 , 접미사가 일치하는 최대 길이. 문자열 전체 제외.
 */

public class KMPReader {

	private String text;
	private String pattern;
	private int[] piArray;

	public KMPReader(String text, String pattern) {
		this.text = text;
		this.pattern = pattern;
		this.piArray = new int[pattern.length()];
	}

	public void setPiArray(int begin, int comparing, int matched) {
		int current = begin + comparing;
		if(current >= pattern.length() || begin >= pattern.length()) return;

		while(current < pattern.length()) {

			if(pattern.charAt(current) == pattern.charAt(comparing)) {
				matched++; comparing++;
				piArray[current] = Math.max(piArray[current], matched);
				current = begin + comparing;
				continue;
			}

			if(current == begin) {
				setPiArray(begin+1, 0, 0);
				return;
			}
			int next_begin = current-piArray[comparing-1];
			int delta = next_begin - begin;
			int next_comparing = comparing - delta;
			setPiArray(next_begin, next_comparing, piArray[comparing-1]);
			return;
		}
	}

	public List<Integer> search(){
		List<Integer> ret = new ArrayList<>();
		setPiArray(1, 0, 0);
		searchLoop(0,0,0,ret);
		return ret;
	}

	private void searchLoop(int begin, int comparing, int matched, List<Integer> ret) {
		//종결 조건
		if(begin > text.length()-pattern.length() || comparing > pattern.length()) return;
		int current = begin + comparing;
		while(comparing < pattern.length()) {
			//일치하는 경우
			if(text.charAt(current) == pattern.charAt(comparing)) {
				matched++;

				if(matched == pattern.length()) {
					ret.add(begin);

					if(comparing == 0) {
						searchLoop(current+1,0,0,ret); return;
					}

					int next_begin = current-piArray[comparing];
					int delta = next_begin - begin;
					searchLoop(current-piArray[comparing-1], comparing - delta, piArray[comparing], ret); return;
				}
				comparing++;
				current = begin + comparing;
				continue;
			}
			//일치하지 않는 경우
			if(begin == current) {
				searchLoop(begin+1,0,0,ret); return;
			}
			int next_begin = current - piArray[comparing-1];
			int delta = next_begin - begin;
			searchLoop(next_begin, comparing - delta, piArray[comparing-1], ret); return;
		}

	}

	public int[] getPiArray() {
		return this.piArray;
	}
}

'알고리즘 > PS' 카테고리의 다른 글

[백준 1009번] 분산 처리  (0) 2020.08.23
[백준 1007번] 벡터 매칭  (0) 2020.08.23
[백준 1006] 습격자 초라기  (0) 2020.08.22
[백준 1004] 어린 왕자  (0) 2020.08.16
[백준 2941번] 크로아티아 알파벳  (0) 2020.07.27
Comments