题目描述
输入一行字符串,该字符串只由小写英文字母a-z组成,且其中的字符可以重复,最长不超过10000个字符。
从该字符串中按顺序挑选出若干字符(不一定相邻)组成一个新串,称为"子串"。如果子串中每两个相邻的字符或者相等,或者后一个比前一个大,则称为"升序子串"。编程求出输入字符串的最长升序子串的长度。
例如,由输入字符串abdbch可以构成的升序子串有:abd、abch、bbch、abbch等。其中最长的升序子串是abbch,其长度为5。
输入
输入一行字符串,该串不含空格,以回车符结束。
输出
输出一个正整数,是字符串中最长的升序子串的长度,在行末要输出一个回车符。
样例1
输入:abdbch 输出:5
样例2
输入:a 输出:1
本题难点在于对于每一个字母在子序列中选与不选都会影响最终结果,常规循环难以解决问题。
我得出的理解重点是,我们可以知道若子序列后面的一个字母c1大于前部分的一个字母c2,而c2的子串s1中任何一个字母都是小于等于c2的,因此s1和c2便可拼凑出c1的一个子串,而这个子串长度是s1字母数l1+1(c2)。
例如abdbch的h,比前面的d大,很自然也比d的子串ab大,因此abd便是h的对应子串。
所以我们可以推导出
任意一项最大子序列长度=前面的项中最大的子序列长度+其本身
因此有如下数学建模过程:
我们可以尝试建立一个数组b[i],来记录终止于字符串第i位的子串长度最大值,我们不妨把这个数组初始化为1,例如输入abdbch,其初始化数组便是{1,1,1,1,1,1}
然后从字符串第二个字母开始,查找前面的比他大的字母有哪些
以abdbch为例
比如第二个字母就知道比他大的是a,
第六个字母就是a,b,d,b,c
从第二个字母跳到第三个字母查找时(第三跳到第四等等也诸如此类),需要先执行如下步骤,求出该字母处的最大子序列长度,
比如第二个字母时,只有a这个子序列,长度是1,
那么其子序列和自身组成的最大子序列长度就是1+1=2,将b[1]改成2
接着跳到第三个字母
这个时候d比a,b都大,而b子序列长度>a
因此d最长子序列长度=b子序列长度+1=3、
而到了第四个字母b
可以发现只有a,b仍然小于等于它
这时候最大序列和就是b的序列和+1=3,
可以看出,到这一步为止,选择b和d结果是一样的,贪心法直接取最近的d没有作用。
第五个字母c,
前面a,b,b(第四个)比他小,而第四个b序列和最大,因此其最大序列和是3+1=4,
这时候可以更好看出不选d的优势,选d则序列终止于d,c在后面无法被考虑,从而导致d的3<c的4,输出错误结果。
第六个字母h,可以发现a,b,d,b,c全比他小,这时选择加上最大的序列和(c的)=4+1=5,
这也是整个题目的最终结果。
需要注意的是不是一定字母放在后面序列和就越大,我们需要一个max变量,每次取得一个位置的最大序列和大于其本身时,就用该序列和赋值给max。
代码如下:
#include <stdio.h>
#include <string.h>
int main() {
int i = 0;
char a[10000] = {'\0'};
scanf("%s", a);
int t = strlen(a);
int b[t];
int max = 1;
for (i = 0; i <= t; i++) {
b[i] = 1;
}
for (i = 1; i < t; i++) {
for (int j = 0; j < i; j++) {
if (a[j] < a[i] || a[j] == a[i]) {
if (b[i] < b[j] + 1) {
b[i] = b[j] + 1;
}
}
}
if (b[i] > max) {
max = b[i];
}
}
printf("%d\n", max);
}