Codeforces 1562E Rescue Niwen!
思路
对于字符串 s 1 s 2 s 3 . . . s n s_1s_2s_3...s_n s1s2s3...sn
我们先来考虑一个子集,仅讨论以 s 1 , s 2 s_1,s_2 s1,s2 两个位置为开头的扩展字符串列中的最长递增子序列,即 s 1 ; . . . ; s 1 , . . . , s n ; s 2 ; . . . ; s 2 . . . s n s_1;...;s_1,...,s_n;s_2;...;s_2...s_n s1;...;s1,...,sn;s2;...;s2...sn 中的最长递增子序列
情况一: s 1 > s 2 s_1>s_2 s1>s2
显然,此时 s 1 , s 2 s_1,s_2 s1,s2 只能二则择一,即,要么选 s 1 s_1 s1 为开头的扩展字符串集合作为答案,要么选 s 2 s_2 s2 的
情况二: s 1 < s 2 s_1<s_2 s1<s2
显然,此时应该贪心的都选
情况三: s 1 = s 2 s_1=s_2 s1=s2
那么我们继续来考虑各自的下一位,即 s 1 + 1 = s 2 s_{1+1}=s_2 s1+1=s2 和 s 1 + 2 = s 3 s_{1+2}=s_3 s1+2=s3 的关系
如果 s 2 = s 3 s_2=s_3 s2=s3 ,继续考虑各自的下一位,即 s 3 s_3 s3 和 s 4 s_4 s4 的关系
如果 s 2 < s 3 s_2<s_3 s2<s3 ,那么类似情况二,可以贪心选完 s 1 s_1 s1 开头的,再从 s 2 s_2 s2 开头且到了 s 3 s_3 s3 的开始选到最后
举个例子,假设串是 a a a b b c aaabbc aaabbc ,那么只考虑前两个位置为开头扩展出来应该是 1 a 1\ a 1 a , 2 a a 2\ aa 2 aa , 3 a a a 3\ aaa 3 aaa , 4 a a a b 4\ aaab 4 aaab , 5 a a a b b 5\ aaabb 5 aaabb , 6 a a a b b c 6\ aaabbc 6 aaabbc , 7 a 7\ a 7 a , 8 a a 8\ aa 8 aa , 9 a a b 9\ aab 9 aab , 10 a a b b 10\ aabb 10 aabb , 11 a a b b c 11\ aabbc 11 aabbc
那么此时 s 1 = s 2 s_1=s_2 s1=s2 且 s 2 = s 3 s_2=s_3 s2=s3 ,然后 s 3 < s 4 s_3<s_4 s3<s4 ,那么首先贪心选 s 1 s_1 s1 开头的,于是选了 1 2 3 4 5 6 ,然后从 s 2 s_2 s2 开头且到 s 4 s_4 s4 的开始选,于是选了 9 10 11
怎么说明是最优呢,显然对于相等的字符串,是肯定要贪心选择位置更小的,而对于前面相等的位置,选 1 肯定比选 7 优,选 2 肯定比 选 8 优,
所以前面相等的部分不用再考虑,对于不相等的部分这种贪心策略能达到可选的最大值
如果 s 2 > s 3 s_2 > s_3 s2>s3 ,首先,对于前面相等的部分是不用考虑的,亦如上述
对于不相等的部分,容易发现选了一边就无法再选另一边了
综上,设 d p i dp_i dpi 表示上一次选了 s 的第 i 位作为开头的扩展串
预处理一下以 i,j 开头的串冲突在哪一位
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
const int mod = 1e9+7;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int t, n;
char s[5050];
ll dp[5050];
int pre[5050][5050];
void debug() {
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++)
cout << pre[i][j] << ' ';
cout << endl;
}
}
int main() {
cin >> t;
while(t--) {
cin >> n;
cin >> s;
ll ans = 0;
for(int i=1; i<n; i++) {
int r = 1;
for(int j=1; j+i<=n; j++) {
if(s[j-1] != s[i+j-1]) {
while(r <= j) {
pre[r][r+i] = j;
++r;
}
}
}
}
// debug();
for(int i=1; i<=n; i++) {
dp[i] = 0;
for(int j=i-1; j >= 0; j--) {
if(j == 0 || s[i-1] > s[j-1]) dp[i] = max(dp[i], dp[j]+n-i+1);
else if(s[i-1] == s[j-1]) {
int r = pre[j][i];
if(r && s[r-1] < s[r-1+i-j]) dp[i] = max(dp[i], dp[j]+n-(r+i-j)+1);
}
}
ans = max(ans, dp[i]);
// cout << i << ' ' << dp[i] << endl;
}
cout << ans << endl;
}
return 0;
}