题目
问题描述
小蓝有一个长度均为 n 且仅由数字字符 0∼9 组成的字符串,下标从 0 到 n−1,你可以将其视作是一个具有 n 位的十进制数字 num,小蓝可以从 num 中选出一段连续的子串并将子串进行反转,最多反转一次。小蓝想要将选出的子串进行反转后再放入原位置处得到的新的数字 numnew 满足条件 numnew<num,请你帮他计算下一共有多少种不同的子串选择方案,只要两个子串在 num 中的位置不完全相同我们就视作是不同的方案。
注意,我们允许前导零的存在,即数字的最高位可以是 00,这是合法的。
输入格式
输入一行包含一个长度为 n 的字符串表示 num(仅包含数字字符 0∼9),从左至右下标依次为 0∼n−1。
输出格式
输出一行包含一个整数表示答案。
样例输入
210102
样例输出
8
样例说明
一共有 88 种不同的方案:
- 所选择的子串下标为 0∼10∼1,反转后的 numnew=120102<210102numnew=120102<210102;
- 所选择的子串下标为 0∼20∼2,反转后的 numnew=012102<210102numnew=012102<210102;
- 所选择的子串下标为 0∼30∼3,反转后的 numnew=101202<210102numnew=101202<210102;
- 所选择的子串下标为 0∼40∼4,反转后的 numnew=010122<210102numnew=010122<210102;
- 所选择的子串下标为 0∼50∼5,反转后的 numnew=201012<210102numnew=201012<210102;
- 所选择的子串下标为 1∼21∼2,反转后的 numnew=201102<210102numnew=201102<210102;
- 所选择的子串下标为 1∼41∼4,反转后的 numnew=201012<210102numnew=201012<210102;
- 所选择的子串下标为 3∼43∼4,反转后的 numnew=210012<210102numnew=210012<210102;
评测用例规模与约定
对于 2020% 的评测用例,1≤n≤100;
对于 4040% 的评测用例,1≤n≤1000;
对于所有评测用例,1≤n≤5000。
运行限制
语言 | 最大运行时间 | 最大运行内存 |
---|---|---|
C++ | 1s | 256M |
C | 1s | 256M |
Java | 2s | 256M |
Python3 | 3s | 256M |
PyPy3 | 3s | 256M |
Go | 3s | 256M |
JavaScript | 3s | 256M |
提交代码
//更小的数
//字符串仅由0-9数字组成
//下标从0开始
//反转0次或1次
//反转后的数字变小
//求有多少种方案
//求所有解
//连续一段元素的群体关系
//动态规划
//前面的数字越小字符串越小
//如果一个字符串可以反转,那么它的两端加上相同的数字或相对固定大小的数字也可以
//起始位置、终点位置、反转方法种数
#include<iostream>
#include<string>
using namespace std;
string num;//原数字字符串
int n;//数字字符串长度
int result = 0;//结果
int dp[5000][5000];//dp[i][j]:起始位置为i,终点位置为j,可以有多少种反转的方法
int main(){
//输入原数字字符串
cin >> num;
n = num.length();//数字字符串长度
//DP
//斜下一条一条找
int i = 0,j = 0;
int cnt = 0;//记录j返回位置
while(true){
if(i == j){
dp[i][j] = 0;
}else{
dp[i][j] = dp[i + 1][j] + dp[i][j - 1] - dp[i + 1][j - 1];
int t = 0;
//判断反转后是否更小
while((i + t) < (j - t) && num[i + t] == num[j - t]){
t++;
}
if(num[i + t] > num[j - t]){
dp[i][j]++;
}
}
i++;
j++;
if(i == 1 && j == n){
//结束
break;
}else if(j == n){
i = 0;
j = cnt + 1;
cnt++;
}
}
//输出结果
result = dp[0][n - 1];
printf("%d",result);
return 0;
}
总结
解题思路:反转一段连续的子串,属于连续一段元素的群体关系,可以使用滑动指针,但是求的不是最优解而是所有解,所以使用动态规划。所选取的子串的状态由起始字符位置和终止字符位置决定,所以用dp[i][j]表示:起始字符位置为i,终止字符位置为j,这段字符串中共有多少种方案。其中当i==j即起始字符和终止字符位置相同时,方案数为0,除此之外,dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1];注意删除其中[i+1,j]和[i,j-1]两段中重复的方案,如果[i,j]整体反转也符合要求,则dp[i][j]在上述式子的基础上加1。