字符串的前缀后缀处理,严格递增子序列
题意:
题目要求在整个是递增子序列的前提下最后一个元素的值要最小,并且在此前提下第一个的值要最大.
思路:
很奇怪的问题,但是细细想来就是要求我们找到严格递增的的序列,可是有两个限制,我们知道如果dp[i]定义为从第i个之前的dp[i]个数字组成的数字满足递增,这种定义方式必然导致从前往后遍历,这会导致每一个递增的数列都是尽可能的小。如此一来,其实就已经得到了最小的最后一个数字,那么我们再从后往前遍历就会得到想要的答案。
第二次dp[i] 表示的是从第i个数字开始往后的dp[i]个数字组成的数字满足严格递增的条件。
那么问提示如何dp呢?
对于第一次,根据其定义的意义,就从第 i 个数字开始,我们逐次增加长度,找打满足条件的就break
对于第二次,根据其定义的意义,若想得到比较大的数字,就从大的区间开始找。
- 比较需要注意的是:题目不要求输出最长的,只要求输出满足连个条件的。
- 此题很好的是考验到了,对dp 的定义,是从哪里开始切入的
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 100;
int dp[maxn];
char s[maxn];
int judge(int x1,int y1,int x2,int y2)
{
while(s[x1] == '0' && x1 <= y1) x1++;
while(s[x2] == '0' && x2 <= y2) x2++;
if(x1 > y1) return false;
if(x2 > y2) return true;
if(y1 - x1 > y2 - x2) return true;
else if(y2 - x2 > y1 - x1) return false;
else {
while(x1 <= y1) {
if(s[x1] > s[x2]) return true;
else if(s[x2] > s[x1]) return false;
x1++,x2++;
}
}
return false;
}
int main(int argc, char const *argv[])
{
freopen("in.txt","r",stdin);
while(scanf("%s",s+1) != EOF) {
int len = strlen(s+1);
if(s[1] == '0' && len == 1) break;
memset(dp,0,sizeof(dp));
dp[1] = 1;
for (int i = 2; i <= len; ++i) {
dp[i] = i;
for(int j = i - 1;j >= 1; j--) {
if(judge(j+1,i,j-dp[j]+1,j)) {
dp[i] = i - j;
break;
}
}
}
int lsatNumberStart = len - dp[len] + 1;
dp[lsatNumberStart] = dp[len];
for(int i = lsatNumberStart-1;i >= 1; i--) {
if(s[i] == '0') {
dp[i] = dp[i+1] + 1;
continue;
}
for(int j = lsatNumberStart;j >= 1; j--) {
if(judge(j,j+dp[j]-1,i,j-1)) {
dp[i] = j - i;
break;
}
}
}
for(int i = 1;i <= dp[1]; i++) {
printf("%c",s[i]);
}
int t = dp[1] + 1;
while(t <= len) {
printf(",");
for(int i = t;i < t + dp[t]; i++)
printf("%c",s[i]);
t = t + dp[t];
}
printf("\n");
}
return 0;
}