思路:
对于一个串,如果我们知道了它翻转后最长的非递减子序列,那它去重之后一定是a1<a2<a3……<an 这种形式,ai只能取0~9
。所以我们可以求出原序列最大值, 最小值,构造一个pat匹配串,VL,VL+1,……VH,然后枚举翻转每一个区间(一共是C(10,2)个),和原串做最长公共子序列。在翻转区间的时候要多加上端点值,比如说, 122432445, pat串为12345,如果你直接翻转234,得到pat串为14325,这样匹配的话有两个2,两个4都不能匹配到,多以pat串应该为1243245;
其实求长度是好求的,但是区间就比较繁琐,可以开一个跟dp数组一样大的两个数组L,R,分别记录每个状态区间值,具体方法看一下代码注释比较好理解。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n;
char buf[N], pat[20], VL, VH;
int dp[N][15], cnt, L[N][15], R[N][15], ansl, ansr, ans;
void init(){
ans=-1;
VL='9', VH='0';
}
void _reverse(char l, char r, int& ll, int& rr){
cnt=0;
for(char k=VL; k<=l; k++) pat[++cnt]=k;
ll=cnt+1;
for(char k=r; k>=l; k--) pat[++cnt]=k;
rr=cnt;
for(char k=r; k<=VH; k++) pat[++cnt]=k;
}
void solve(int l, int r){
for(int i=0; i<=cnt; i++) dp[0][i]=0;
for(int i=0; i<=n; i++) for(int j=0; j<=cnt; j++) L[i][j]=R[i][j]=-1;
for(int i=1; i<=n; i++){
for(int j=1; j<=cnt; j++){
dp[i][j]=max(dp[i-1][j]+(buf[i]==pat[j]), dp[i][j-1]);
/******************************************///找翻转区间
if(dp[i-1][j]+(buf[i]==pat[j])>=dp[i][j-1]){//如果dp[i][j]由前一项转移而来
L[i][j]=L[i-1][j]; R[i][j]=R[i-1][j];//它首先应该继承前一个状态的区间,这样能使L尽量的小
if(buf[i]==pat[j]){
if(l==j&&L[i][j]==-1) L[i][j]=i;//如果前一个状态没有L,则L可以赋值
if(r==j) R[i][j]=i;//R只要能更新就一直更新,尽量的大
}
if(L[i][j]==-1&&L[i][j-1]!=-1){
L[i][j]=L[i][j-1]; R[i][j]=R[i][j-1];//这是前后两个值相等的情况如果前面的赋值没有使得L获得一个区间值,可以从(i, j-1)获得
}
}
else if(dp[i-1][j]+(buf[i]==pat[j])<dp[i][j-1]){//如果后者大,直接从后者获得
L[i][j]=L[i][j-1]; R[i][j]=R[i][j-1];
}
/******************************************/
}
}
if(ans<dp[n][cnt] && L[n][cnt]!=-1 && R[n][cnt]!=-1){
ans=dp[n][cnt];
ansl=L[n][cnt];
ansr=R[n][cnt];
}
}
int main(){
int T;
scanf("%d", &T);
while(T--){
init();
scanf("%d", &n);
scanf("%s", buf+1);
for(int i=1; i<=n; i++)
VL=min(buf[i], VL), VH=max(buf[i], VH);
if(VL==VH){
printf("%d 1 1\n", n);
continue;
}
for(char i=VL; i<=VH; i++){
for(char j=i; j<=VH; j++){
int ll, rr;
_reverse(i, j, ll, rr);
solve(ll, rr);
}
}
printf("%d %d %d\n", ans, ansl, ansr);
}
return 0;
}
/*
9
122265489
2
12
*/