单调递增最长子序列
时间限制:
3000 ms | 内存限制:
65535 KB
难度:
4
-
描述
-
求一个字符串的最长递增子序列的长度
如:dabdbf最长递增子序列就是abdf,长度为4-
输入
-
第一行一个整数0<n<20,表示有n个字符串要处理
随后的n行,每行有一个字符串,该字符串的长度不会超过10000
输出
- 输出字符串的最长递增子序列的长度 样例输入
-
3 aaa ababc abklmncdefg
样例输出
-
1 3
7
-
法一:
-
法二:#include <stdio.h> #define MAX 10009 int d[MAX];//记录长度数组 int main() { int n, i, j, ma; scanf("%d", &n); char s[MAX]; while(n--) { scanf("%s", s); ma = 0; for(i = 0; s[i] != 0; i++) d[i] = 1;//最开始初始化为1,倘若没有最长子序列,则把它自身当做最长子序列,即为1 for(i = 0; s[i] != 0; i++) { for(j = i + 1; s[j] != 0; j++) {//从i之后开始把整个数组遍历一遍,可能有点费时 if(s[j] > s[i] && d[j] < d[i] + 1) {//至于这里为什么会d[j]<d[i]+1我还没搞太明白,这个创造者也的确很牛 d[j] = d[i] + 1; } } } for(i = 0; s[i] != 0; i++) { if(ma < d[i]) { ma = d[i]; } } printf("%d\n", ma); } return 0; }
-
法三:#include <stdio.h> #include <string.h> #define MAX 10009 int d[MAX]; int main() { int n, len, i, j, ma; scanf("%d", &n); char s[MAX]; while(n--) { ma = 0; scanf("%s", s); len = strlen(s); for(i = 0; i < len; i++) d[i] = 1; for(i = len - 2; i >= 0; i--) {//相对于上一个,这里做了一下改进,从最末开始,这样遍历的数组长度也比较少,时间复杂度稍微改进一点 for(j = i + 1; j <= len - 1; j++) { if(s[i] < s[j] && d[i] < d[j] + 1) { d[i] = d[j] + 1; } } } for(i = 0; i < len; i++) { if(ma < d[i]) { ma = d[i]; } } printf("%d\n", ma); } return 0; }
-
这里又做了一下优化,直接用变量叠加即可,类似于最优代码
#include <stdio.h> #include <string.h> #define MAX 10009 int main() { char s[MAX], a[MAX]; int n, i, j, cnt; scanf("%d", &n); while(n--) { scanf("%s", s); a[0] = 0; cnt = 1; for(i = 0; s[i] != 0; i++) { for(j = cnt - 1; j >= 0; j--) { if(s[i] > a[j])//这里是关键 { a[j + 1] = s[i]; if(j + 1 == cnt) cnt++; break ; } } } printf("%d\n", cnt - 1); } return 0; }
法三:
- dp[i] 以 i 结尾的当然也包括 i 在内的最长递增子序列;
- 我经常犯的的一个错误是:dp[i] = d[j] + 1(j<i && s[j]<s[i] && j 是第一个成立的)
- 这是错误的 如:"123124” 对应的dp是 dp[0]=1;dp[1]=2;dp[2]=3; dp[3]=1;dp[4]=2;dp[5]=3;这就是一个错误列子;
-
正解:dp[i] = max( d[j] ) + 1(j<i && s[j]<s[i] 成立中的最大的一个 )
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int dp[10010]; char s[10010]; int main() { int n, len, i, j, ma; while(~scanf("%d", &n)) { scanf("%s", s); len = strlen(s); dp[0] = 1; for(i = 1; i < len; i++) { ma = 0; for(j = i - 1; j >= 0; j--) { if(s[i] > s[j] && ma < dp[j]) { ma = dp[j]; } } dp[i] = ma + 1; } ma = dp[0]; for(i = 1; i < len; i++) { if(ma < dp[i]) { ma = dp[i]; } } printf("%d\n", ma); } return 0; }
单调递增子序列(二)
时间限制: 1000 ms | 内存限制: 65535 KB难度: 4-
描述
-
给定一整型数列{a1,a2...,an}(0<n<=100000),找出单调递增最长子序列,并求出其长度。
如:1 9 10 5 11 2 13的最长单调递增子序列是1 9 10 11 13,长度为5。
-
输入
-
有多组测试数据(<=7)
每组测试数据的第一行是一个整数n表示序列中共有n个整数,随后的下一行里有n个整数,表示数列中的所有元素.每个整形数中间用空格间隔开(0<n<=100000)。
数据以EOF结束 。
输入数据保证合法(全为int型整数)!
输出
- 对于每组测试数据输出整形数列的最长递增子序列的长度,每个输出占一行。 样例输入
-
7 1 9 10 5 11 2 13 2 2 -1
样例输出
-
5 1
-
有多组测试数据(<=7)
-
-
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int a[1000010], ans[1000010]; int fun(int len) { int i, j, k, left, right, mid; ans[0] = a[0]; k = 0; for(i = 1; i < len; i++) { if(ans[k] < a[i]) { k++; ans[k] = a[i]; continue; } left = 0; right = k; while(left <= right) { if(a[i] < ans[left]) { j = left; break ; } if(ans[right] < a[i]) { j = right + 1; break ; } mid = (left + right) / 2; if(ans[mid] < a[i]) { left = mid + 1; } else if(a[i] < ans[mid]) { right = mid - 1; } else { j = mid; break ; } } ans[j] = a[i]; } return k + 1; } int main() { int n, i; while(~scanf("%d", &n)) { for(i = 0; i < n; i++) { scanf("%d", &a[i]); } printf("%d\n", fun(n)); } return 0; }
最长公共子序列
时间限制: 3000 ms | 内存限制: 65535 KB难度: 3-
描述
-
咱们就不拐弯抹角了,如题,需要你做的就是写一个程序,得出最长公共子序列。
tip:最长公共子序列也称作最长公共子串(不要求连续),英文缩写为LCS(Longest Common Subsequence)。其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。-
输入
-
第一行给出一个整数N(0<N<100)表示待测数据组数
接下来每组数据两行,分别为待测的两组字符串。每个字符串长度不大于1000.
输出
- 每组测试数据输出一个整数,表示最长公共子序列长度。每组结果占一行。 样例输入
-
2 asdf adfsd 123abc abc123abc
样例输出
-
3 6
-
第一行给出一个整数N(0<N<100)表示待测数据组数
优化了空间的。。。<pre name="code" class="cpp">#include <iostream> #include <cstdio> #include <cstring> #define MAX 1005 using namespace std; char s1[MAX], s2[MAX]; int dp[MAX][MAX]; int lcs(int len1, int len2) { int len = len1 > len2 ? len1 : len2; int i, j; for(i = 0; i < len; i++) { dp[i][0] = 0; dp[0][i] = 0; } for(i = 1; i <= len1; i++) { for(j = 1; j <= len2; j++) { if(s1[i - 1] == s2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { if(dp[i - 1][j] < dp[i][j - 1]) { dp[i][j] = dp[i][j - 1]; } else { dp[i][j] = dp[i - 1][j]; } } } } return dp[len1][len2]; } int main() { int n, len1, len2; scanf("%d", &n); while(n--) { scanf("%s%s", s1, s2); len1 = strlen(s1); len2 = strlen(s2); printf("%d\n", lcs(len1, len2)); memset(s1, 0, sizeof(s1)); memset(s2, 0, sizeof(s2)); } return 0; }
#include <stdio.h> #include <string.h> char s1[1001], s2[1001]; int dp[1001], t, old, tmp; int main(){ scanf("%d", &t); getchar(); while(t--){ gets(s1); gets(s2); memset(dp, 0, sizeof(dp)); int lenS1=strlen(s1), lenS2=strlen(s2); for(int i=0; i<lenS1; i++){ old=0; //若s1[i]==s2[j], dp[i][j] = dp[i-1][j-1]+1 //否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1]) //此处进行了空间优化,old 代表 dp[i-1][j-1] //dp[j-1] 代表 dp[i][j-1], dp[j] 代表 dp[i-1][j] for(int j=0; j<lenS2; j++){ tmp = dp[j]; if(s1[i]==s2[j]) dp[j] = old+1; else if(dp[j-1]>dp[j])dp[j]=dp[j-1]; old = tmp; } } printf("%d\n", dp[lenS2-1]); } return 0; }
-
咱们就不拐弯抹角了,如题,需要你做的就是写一个程序,得出最长公共子序列。
-
第一行一个整数0<n<20,表示有n个字符串要处理