codeforces 1562 E. Rescue Niwen! (dp)

题目链接:http://codeforces.com/contest/1562/problem/E

首先考虑贪心,如果选择了从 (i) 开始的字符串,那么选择所有 (i) 开始的字符串一定不会更差

(dp[i]) 表示以 (i) 开始的字符串为结尾的最长上升子序列的长度,如果可以从 (j(j<i)) 转移过来,那一定存在一个从 (j) 开始的字符串,使得该字符串字典序小于某个从 (i) 开始的字符串,直接判断复杂度是 (O(n)) 的,我们可以考虑求出两两后缀字符串的 (lcp),每次转移时比较 (lcp) 的下一个字符即可

求所有后缀字符串两两的 (lcp) 可以使用 (dp),设 (dp[i][j]) 表示后缀 (i)(j)(lcp),则 (dp[i][j] = (dp[i+1][j+1]+1)*(s[i]==s[j]))

时间复杂度 (O(n^2))

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 5010;

int T, n;
int lcp[maxn][maxn], dp[maxn];
char s[maxn];

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	scanf("%d", &T);
	while(T--){
		scanf("%d", &n);
		scanf("%s", s+1);
		for(int i = n ; i >= 1 ; --i){
			for(int j = n ; j >= 1 ; --j){
				lcp[i][j] = (lcp[i+1][j+1] + 1) * (s[i] == s[j]);
			}
		}
		
		for(int i = 1 ; i <= n ; ++i){
			dp[i] = n - i + 1;
			for(int j = 1 ; j < i ; ++j){
				if(s[i+lcp[i][j]] > s[j+lcp[i][j]]) dp[i] = max(dp[i], dp[j] + n-i+1-lcp[i][j]);
			}
		}
		
		int ans = 0;
		for(int i = 1 ; i <= n ; ++i) ans = max(ans, dp[i]);
		printf("%d
", ans);
		
		for(int i = 0 ; i <= n ; ++i) dp[i] = 0;
		for(int i = 0 ; i <= n + 1 ; ++i){
			for(int j = 0 ; j <= n + 1 ; ++j) lcp[i][j] = 0;
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/15223806.html