LCS-最长公共子序列的免去边界初始化的一些模板(记忆化搜索+记录路径的DP+优化空间的DP)

   我们知道,输入一个字符串的时候一般是以0为下标的,而在最长公共子序列的计算中,任意字符串对空字符串的最长公共子序列都是0(对面啥都没有还有什么“公共”可言)。但是一般的输入下,0号位置是存储位置,这就因为无法表示而无法解决零长度这个问题,从而需要对结果矩阵进行边界初始化,也就是说把dp[0][i],dp[j][0]均初始化为0(i,j∈length(StringA),length(StringB))

 有一个办法是:由于scanf("%s",location)后边的location指的是字符数组的地址,由于我们一般都是直接把数组名输上去,所以都是从首地址(0号位置)开始储存字符串,如果想从1开始计数,直接用scanf(“%s”,location+1)即可,这样就免去了上面的边界初始化。

下面的模板都是根据以上技巧所设(均在NYOJ(南阳理工学院)上测试过):

记忆化搜索模板(倒搜版):

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=1000000007;
const int tool_const=1999112620000907;
const int tool_const2=33;
inline ll lldcin()
{
    ll tmp=0,si=1;
    char c=getchar();
    while(c>'9'||c<'0')
    {
        if(c=='-')
            si=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        tmp=tmp*10+c-'0';
        c=getchar();
    }
    return si*tmp;
}
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
char str1[1500],str2[1500];
ll dp[1200][1200];
ll solution(ll currentx,ll currenty)//记忆化搜索函数,倒着进行搜索。
{
    if(currentx<1||currenty<1)//超出范围,直接返回0
        return 0;
    ll ans=0;
    if(dp[currentx][currenty]>=0)//已经搜索过了,直接返回搜索结果。
        return dp[currentx][currenty];
    if(str1[currentx]==str2[currenty])//字符相等的话,向左上搜索.
    {
        ans=solution(currentx-1,currenty-1)+1;
        dp[currentx][currenty]=ans;
    }
    else//否则取正上方和正左方搜索的最大值,并储存。
    {
        dp[currentx][currenty]=max(solution(currentx-1,currenty),solution(currentx,currenty-1));
    }
    return dp[currentx][currenty];返回储存值。
}
int DETERMINATION()
{
    ll n;
    lldin(n);
    while(n--)
    {
        reset(dp,-1);//由于在上边的记忆化搜索需要检测当次结果是否已知,所以初始化是不能为0的。
        reset(str1,'\0');
        reset(str2,'\0');
        getchar();
        scanf("%s",str1+1);
        scanf("%s",str2+1);
        ll len1=strlen(str1+1);
        ll len2=strlen(str2+1);
        solution(len1,len2);
        printlnlld(dp[len1][len2]);
    }
    return 0;
}

动态规划法+记录路径(这个记录路径的函数可以套回记忆化搜索里)

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=1000000007;
const int tool_const=1999112620000907;
const int tool_const2=33;
inline ll lldcin()
{
    ll tmp=0,si=1;
    char c=getchar();
    while(c>'9'||c<'0')
    {
        if(c=='-')
            si=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        tmp=tmp*10+c-'0';
        c=getchar();
    }
    return si*tmp;
}
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
char str1[3500],str2[3500];
ll dp[1350][1350],sign[1350][1350];
void display(ll currentx,ll currenty)
这是复现最长公共子序列的内容的函数
{
    if(currentx==0||currenty==0)
        return ;
    else if(sign[currentx][currenty]==1)
    {
        display(currentx-1,currenty-1);
        printf("%c",str1[currentx]);
       由于需要正向输出,而这是反向回溯,所以需要把输出放在递归步骤后边,这样就是等递归结束后再一一输出。
    }
    else if(sign[currentx][currenty]==2)
        display(currentx-1,currenty);
    else
        display(currentx,currenty-1);
}
int DETERMINATION()
{
    ll t;
    lldin(t);
    while(t--)
    {
        reset(dp,0);
        reset(sign,0);
        reset(str1,'\0');
        reset(str2,'\0');
        getchar();
        scanf("%s",str1+1);
        scanf("%s",str2+1);
        ll len1=strlen(str1+1);
        ll len2=strlen(str2+1);
        for(int i=1; i<=len1; i++)
            for(int j=1; j<=len2; j++)
            {
                if(str1[i]==str2[j])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                    sign[i][j]=1;//记录各种回溯方案,1是左上,2是正上,3是正左。
                }
                else
                {
                    if(dp[i-1][j]>=dp[i][j-1])
                    {
                        dp[i][j]=dp[i-1][j];
                        sign[i][j]=2;
                    }
                    else
                    {
                        dp[i][j]=dp[i][j-1];
                        sign[i][j]=3;
                    }
                }
            }
        display(len1,len2);
        printf("\n");
        printlnlld(dp[len1][len2]);
    }
    return 0;
}

空间优化的动态规划法(不可记录路径)

由上面的代码可知,在整个操作过程中只用到了上一行和前一列,所以可以用一个优化把上述M×N的DP数组变成2×max(M,N)的数组。

#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const long long int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=1000000007;
const int tool_const=1999112620000907;
const int tool_const2=33;
inline ll Unnamed_Scanner()
{
    ll tmp=0,si=1;
    char c=getchar();
    while(c>'9'||c<'0')
    {
        if(c=='-')
            si=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        tmp=tmp*10+c-'0';
        c=getchar();
    }
    return si*tmp;
}
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
char a[2312323],b[3212323];
ll uuu[1200][1200];
int DETERMINATION()
{
    ll n;
    lldin(n);
    while(n--)
    {
        reset(a,'\0');
        reset(b,'\0');
        reset(uuu,0);
        getchar();
        scanf("%s",a+1);
        scanf("%s",b+1);
        ll len=strlen(a+1);
        ll len2=strlen(b+1);
        //cout<<len<<endl;
        ll current=1,previous=0;//当前行与上一行
        for(int i=1; i<=len; i++)
        {
            for(int j=1; j<=len2; j++)
            {
                if(a[i]==b[j])
                    uuu[current][j]=uuu[previous][j-1]+1;
                else
                    uuu[current][j]=max(uuu[previous][j],uuu[current][j-1]);
            }
            swap(current,previous);//前一行现在就是当前行,交换。
        }
        printlnlld(uuu[previous][len2]);
    }
    return 0;
}

 

posted @ 2019-05-18 09:35  完全墨染的樱花  阅读(263)  评论(0)    收藏  举报