코딩/알고리즘

[알고리즘] LIS LCS

최선을 다하는 2022. 2. 5. 11:47

- LIS (Longest Increasing Subsequence)

1~N 번까지 순서대로 i번째 dp값을 최신화해준다. j는 i보다 작은 수 즉 배열의 앞에 있는 숫자라고 할 때 i번째 숫자가 j번째보다 크고 dp [i]가 dp [j] + 1보다 작다면 dp [i]의 값을 dp [j]+1로 최신화해준다.

int arr[101];
int dp[101] ;
int N,ans = 0;

void LIS() {
	for (int i = 0; i < N; i++) {
		dp[i] = 1;
		for (int j = 0; j < i; j++) {
			if (arr[i]> arr[j]) {
				if (dp[i] < dp[j] + 1)
					dp[i] = dp[j] + 1;
			}
		}
		ans = max(ans, dp[i]);
	}
}

위의 풀이는 DP를 활용한  O(N^2)의 풀이 방법이다.

lower_bound 를 활용하여 O(NlogN)의 시간에 풀 수도 있다.

배열을 순회하면서 원소를 확인하는데 LIS의 끝에 추가 될 수 있다면 추가를 하고 그렇지 않다면 자신보다 같거나 큰 원소를 lower_bound를 활용하여 찾아내 자신과 교체한다. 이렇게 되면 LIS 배열 자체를 알 수는 없지만 LIS 배열의 길이는 알 수 있다. 배열 자체를 알고 싶으면 또 다른 배열을 하나 추가하여 자신이 몇번째 LIS 원소로 추가되었는지를 기억한다음 배열의 뒤에서 부터 역추적 한다면 LIS 배열도 구할 수 있다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#define MAX 1000010

using namespace std;

int N;
int arr[MAX],dp[MAX],memo[MAX];
stack <int> ans;
int len = 0;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	
	cin >> N;
	for (int i = 0; i < N; i++) cin >> arr[i];
	dp[len++] = arr[0];
	memo[0] = 0;
	for (int i = 1; i < N; i++) {
		if (arr[i] > dp[len - 1]) {
			dp[len++] = arr[i];
			memo[i] = len - 1;
		}
		else {
			int* iter = lower_bound(dp, dp + len, arr[i]);
			*iter = arr[i];
			memo[i] = iter - dp;
		}
	}
	cout << len << "\n";
	int cnt = len - 1;
	for (int i = N - 1; i >= 0; i--) {
		if (cnt == memo[i]) {
			ans.push(arr[i]);
			cnt--;
		}
	}
	while (!ans.empty()) {
		cout << ans.top() << " ";
		ans.pop();
	}
	return 0;	
}

- LCS (Longest Common Subsequence)

문자열 두 개가 있을 때 한 문자열의 i번째 문자와  다른 한 문자열의 j번째 문자를 비교하여

문자가 같다면 i와 j를 전 문자열들 중 LCS에서 1을 더하면 되므로 dp [i-1][j-1] + 1을 해주면 된다. 

문자가 같지 않다면 i-1j의 LCS와 i와 j-1의 LCS의 큰 값을 이어오면 되므로 dp [i-1][j]와 dp [i][j-1] 중 큰 값을 가져온다.

int dp[1001][1001];
int solve(string s,string t) {
	for (int i = 1; i <= s.size(); i++) {
		for (int j = 1; j <= t.size(); j++) {
			if (s[i-1] == t[j-1]) // dp[1] 이 0번째 문자열을 가리킴
				dp[i][j] = dp[i - 1][j - 1] + 1;
			else
				dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
		}
	}
	return dp[s.size()][t.size()];
}

- LCS (Longest Common Substring)

위 Longest Common Subsequence 와의 차이는 두 문자가 같지 않을 때 1로 초기화시켜준다는 것과 답이 dp값들중 최고값이라는 것이다.

int solve(string s, string t) {
	int res = 0;
	for (int i = 1; i <= s.size(); i++) {
		for (int j = 1; j <= t.size(); j++) {
			if (s[i - 1] == t[j - 1]) {
				dp[i][j] = dp[i - 1][j - 1] + 1;
				res = max(res, dp[i][j]);
			}
			else
				dp[i][j] = 0;
		}
	}
	return res;
}

 

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

[알고리즘 - 문자열 처리] KMP  (0) 2022.02.09
[알고리즘] 위상정렬  (0) 2022.02.08
[알고리즘] LCA (Lowest Common Ancestor)  (2) 2022.02.07