最长严格上升子序列
LIS问题,动归时间复杂度o(n2),可以用单调队列优化到o(nlogn)
http://blog.csdn.net/dangwenliang/article/details/5728363
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int n,dp[5005],orz[5005],ans = 0; 6 int main(){ 7 cin>>n; 8 for(int i = 1;i <= n;i++) scanf("%d",&orz[i]); 9 for(int i = 1;i <= n;i++){ 10 dp[i] = 1; 11 for(int j = 1;j < i;j++){ 12 if(orz[i] > orz[j])dp[i] = max(dp[i],dp[j]+1); 13 ans = max(ans,dp[i]); 14 } 15 } 16 cout<<ans; 17 return 0; 18 }
1 #include <iostream> 2 using namespace std; 3 int find(int *a,int len,int n)//修改后的二分查找,若返回值为x,则a[x]>=n 4 { 5 int left=0,right=len,mid=(left+right)/2; 6 while(left<=right) 7 { 8 if(n>a[mid]) left=mid+1; 9 else if(n<a[mid]) right=mid-1; 10 else return mid; 11 mid=(left+right)/2; 12 } 13 return left; 14 } 15 16 int main(void) 17 { 18 int n,a[100],c[100],i,j,len;//新开一变量len,用来储存每次循环结束后c中已经求出值的元素的最大下标 19 while(cin>>n) 20 { 21 for(i=0;i<n;i++) 22 cin>>a[i]; 23 b[0]=1; 24 c[0]=-1; 25 c[1]=a[0]; 26 len=1;//此时只有c[1]求出来,最长递增子序列的长度为1. 27 for(i=1;i<n;i++) 28 { 29 j=find(c,len,a[i]); 30 c[j]=a[i]; 31 if(j>len)//要更新len,另外补充一点:由二分查找可知j只可能比len大1 32 len=j;//更新len 33 } 34 cout<<len<<endl; 35 } 36 return 0; 37 }
最长公共子序列
要求c既是a的子序列,又是b的子序列,输出c的长度
设i,j为a到i,b到j可以取到的公共子序列长度,如果当前位置匹配,那么就是两个位置之前能匹配到的+1,如果不能匹配,考虑选一个最长的可能匹配的长度,[i-1][j]和[i][j-1]都是有可能的
for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ dp[i][j] = max(dp[i-1][j],dp[i][j-1]); if(a[i] == b[j]) dp[i][j] = max(dp[i][j],dp[i-1][j-1] + 1); } }
最长公共上升子序列
题目在公共子序列的基础上又加了一重上升的限制,可以考虑加一个变量,记录在a[i]的情况下,b[j]小于a[i]的情况下lcis的最大长度,这样,如果发现两个序列对应位置相等,并且已经求出小于a[i]的lcis的长度,就可以直接求出当前位置的了
#include<iostream> #include<string> #include<cstdio> #include<cstring> using namespace std; int max(int a,int b) { return a>b?a:b; } int a[3010],b[3010]; int f[3010],n,m; int LCIS() { int i,j,k; memset(f,0,sizeof(f)); for(i=0;i<n;i++) { k=0; for(j=0;j<m;j++) { if(a[i]==b[j]) //如果a[i]==b[j] { if(f[j]<k+1) //就在0到j-1之间,找一个b[k]小于a[i]的f[k]值最大的解 f[j]=k+1; } if(a[i]>b[j]) //0到j-1中,对于小于a[i]的,保存f值的最优解 { if(k<f[j]) k=f[j]; } } } int ans=0; for(i=0;i<m;i++) ans=max(ans,f[i]); return ans; } int main() { int t,i,j; scanf("%d",&n); for(i=0;i<n;i++) { scanf("%d",&a[i]); } m = n; for(j=0;j<m;j++) { scanf("%d",&b[j]); } printf("%d",LCIS()); return 0; }
带通配符的字符串匹配
有AB两个串,A串里有? 和 * 表示通配符,?可以指代任意一个字符,*可以指代多个字符(也可以什么都不指代),问这两个串是否匹配
做这个题,一定要反复慎重考虑边界问题
首先,星号可以表示什么都没有,所以B串的递推要从0开始
如果遇到星号,就逆着B串匹配的位置往前找,如果星号之前A串能与B串的某一位置匹配那么他就能匹配,还要注意到0的问题,因为星号之前可能全是星号
如果不是,就看此位置和上个位置是否都匹配,注意这个时候B串的位置必须大于0
这样就可以过了
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> using namespace std; char a[200],b[200]; int dp[200][200]; int main(){ scanf("%s%s",a,b); int lena = strlen(a),lenb = strlen(b); int pta = 0,ptb = 0; dp[0][0] = 1; for(int i = 1;i <= lena;i++){ for(int j = 0;j <= lenb;j++){ if(a[i-1] == '*'){ for(int k = j;k >= 0;k--){ if(dp[i-1][k]){ dp[i][j] = 1; break; } } }else if((a[i-1] == b[j-1] || a[i-1] == '?') && (j&&dp[i-1][j-1])){ dp[i][j] = 1; } } } if(dp[lena][lenb]) cout<<"matched"; else cout<<"not matched"; return 0; }