两道子序列经典题目

力扣1143:最长公共子序列

思路:这是一道经典题,dp[i][j]表示第一个字符串[0,i-1]和第二个字符串[0,j-1]之间公共的子序列个数,每次多比较一个,dp[i][j]可以由dp[i-1][j-1]、dp[i][j-1]、dp[i-1][j]得到。

class Solution {
    public int longestCommonSubsequence(String s1, String s2) {
        int len1=s1.length();
        int len2=s2.length();
        int[][] dp=new int[len1+1][len2+1];
        //i和j从1开始,防止-1时越界
        for(int i=1;i<=len1;i++)
        {
            for(int j=1;j<=len2;j++)
            {

                if(s1.charAt(i-1)==s2.charAt(j-1))
                    dp[i][j]=dp[i-1][j-1]+1;
                

                else
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
            }

        }
        return dp[len1][len2];
    }
}
力扣1143

 

牛客练习赛70B

题意:求最短子串长度,要求包含"puleyaknoi"子序列。

思路:子序列的字符用一个前驱数组维护,指定子序列的上一个字符是什么,这里10个字符没有重复,一维就够了。对子序列的字符每次同步前驱字符的位置,找到子序列的起点。不可以直接记录子序列起点的位置,可能会被最近更新的起点位置覆盖。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<sys/types.h>
#include<unistd.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;


char s[100005];
int num[30];///模式串
int dp[30];///
char a[]={"puleyaknoi"};
int pre[30];///模式串字符的前驱字符

int main()
{
    int lena=strlen(a);
    for(int i=0;i<lena;i++)
    {
        num[ a[i]-'a' ]=1;
        if( i!=0 )///第一个字符没有前驱
            pre[ a[i]-'a' ]=a[i-1]-'a';
    }

    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,-1,sizeof(dp));///防止起点是0相冲,dp[i]记录字符i作为子序列的起点的位置
        scanf("%s",s);
        int lens=strlen(s);
        int ans=inf;
        for(int i=0;i<lens;i++)
        {
            int x=s[i]-'a';
            if( num[x]==0 )///不是喜欢的字符
                continue;
            if( s[i]=='p' )///第一个字符,做一下标记,持续更新,后面遇到的字符也会更新
            {
                dp['p'-'a']=i;
            }
            else
            {
                ///当前字符x的前驱存在,才可以往后走
                if(dp[ pre[x] ]!=-1 )
                    dp[ x ]=dp[ pre[x] ];///和前驱同一个起点,根据前驱找到所在子序列的起点,而不是直接指向起点,可能会被最近的新的起点覆盖
            }
            if( s[i]=='i' && dp[ x ]!=-1 )///10个够了,并且i字符有前驱,一直连到p
                ans=min(ans,i-dp[x]+1);

        }
        if(ans==inf)
            printf("-1\n");
        else
            printf("%d\n",ans);
    }
    return 0;
}

思路扩展:子序列可以出现多次相同字符,则dp数组多开一维,dp[x][j]=dp[pre[x][j].c][pre[x][j].d],dp[x][j]表示当前字母x与模式串中第j个x字母匹配时,最近的起始位置。pre[x][j].c表示模式串中第j个x字母的前一个字母,pre[x][j].d表示模式串中第j个x字母的前一个字母是第几次出现。

 

posted @ 2020-09-26 00:25  守林鸟  阅读(253)  评论(0编辑  收藏  举报