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\n", 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;
}
posted @ 2021-09-03 16:49  Tartarus_li  阅读(41)  评论(0编辑  收藏  举报