首页 > 其他分享 >洛谷题单指南-动态规划2-P2679 [NOIP2015 提高组] 子串

洛谷题单指南-动态规划2-P2679 [NOIP2015 提高组] 子串

时间:2024-04-26 17:02:53浏览次数:26  
标签:子串 方案 NOIP2015 终点 int 洛谷题 作为 bi P2679

原题链接:https://www.luogu.com.cn/problem/P2679

题意解读:在a中按顺序挑选k个子串,使得这些子串连在一起正好和b相等,求方案数。

解题思路:

这样的题目,无外乎两个思路:DFS暴搜(得部分分)、动态规划

动态规划不好想,还是先暴搜吧!

1、DFS暴搜

搜索的思路就是:从a起始位置开始,一直找到跟b前缀相等的字符,然后枚举连续有多少个字符相等,都作为一种可能方案,

再把相等的前缀拿掉,相应子串数量+1,对a、b剩下的部分继续进行dfs,

当然,为了尽可能时间最优,还需要进行合理的剪枝:

剪枝操作1:如果已经累计找到超过k个子串了还没有匹配上,就不用再继续了

剪枝操作2:如果a剩下的长度比b剩下的长度小,也不用再继续判断了

40分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1005, M = 205, MOD = 1000000007;
string a, b;
int n, m, k, ans;

//从a的ai开始,枚举子串和b的bi开始的前缀匹配,匹配上数量cnt+1
void dfs(int ai, int bi, int cnt)
{
    if(cnt == k && bi == b.size())
    {
        ans = (ans + 1) % MOD;
        return;
    }
    if(cnt >= k) return; //剪枝:超过k个子串,不需要再继续处理
    if(a.size() - ai < b.size() - bi) return; //剪枝:a剩下的长度比b剩下的小,a的子串永远不能等于b
    for(int i = ai; i < a.size(); i++)
    {
        if(a[i] == b[bi]) //如果找到a、b相等的位置
        {
            int j = 0;
            while(i + j < a.size() && bi + j < b.size() && a[i + j] == b[bi + j]) //往后枚举所有相等的前缀,作为一种方案,并继续dfs
            {
                dfs(i + j + 1, bi + j + 1, cnt + 1);
                j++;
            }
        }
    }
}

int main()
{
    cin >> n >> m >> k >> a >> b;
    dfs(0, 0, 0);
    cout << ans;
    return 0;
}

2、动态规划

首先,思考状态表示,

本题直观上需要三个状态:以a[i]为终点,与b[1~j]匹配,且子串数为p的总方案数,

但是,由于不是每一个a[i]为终点都能和b[1~j]匹配,要匹配必须有a[i] = b[j],

所以对于每一个i,a[i]这个终点可以用也可以不用,干脆定义两个状态:

f[i][j][p]:表示以a[i]为终点(a[i]使用),和b[1~j]匹配,且子串数是p的方案数

g[i][j][p]:表示以a[i]为终点(a[i]不使用),和b[1~j]匹配,且子串数是p的方案数

状态转移为:

当a[i] == b[j]时,可以使用a[i]作为终点,也可以不使用a[i]作为终点,f,g都要转移

  f[i][j][p] = f[i-1][j-1][p-1] + g[i-1][j-1][p-1] + f[i-1][j-1][p],

    使用a[i]作为终点的方案 = a[i]单独作为子串的方案 + a[i]跟前面一起作为子串的方案

    a[i]单独作为子串的方案 = a[i-1]作为终点的方案 + a[i-1]不作为终点的方案 = f[i-1][j-1][p-1] + g[i-1][j-1][p-1]

    a[i]跟前面一起作为子串的方案 = a[i-1]作为终点的方案 =  f[i-1][j-1][p],注意这里还是p,而不是p-1,因为a[i]没有单独成一个子串    

  g[i][j][p]  = f[i-1][j][p] + g[i-1][j][p],

    不使用a[i]作为终点的方案 = 使用a[i-1]作为终点的方案 + 不使用a[i-1]作为终点的方案

当a[i] != b[j]时,只能不使用a[i]作为终点,g需要转移,f对应的方案是0

  f[i][j][p] = 0,

    因为a[i] != b[j],所以使用a[i]作为终点来匹配b[1~j]的方案数是0

  g[i][j][p] = f[i-1][j][p] + g[i-1][j][p],

    不使用a[i]作为终点的方案 = 使用a[i-1]作为终点的方案 + 不使用a[i-1]作为终点的方案

初始化:f[i][0][0] = 1, i从0~n,以i为终点、和空串匹配、选0个子串的方案是1

答案就是f[n][m][k] + g[n][m][k]

注意:空间复杂度是n * m * k * 4 * 2 = 1000 * 200 * 200 * 4 * 2 = 320M,肯定是不行的

但从递推式来看i只依赖i-1,可以用优化掉一维,先写出三维的代码,只针对1~7数据点,n=500,m=50

70分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 505, M = 55, MOD = 1000000007;
string a, b;
int n, m, k;
int f[N][M][M], g[N][M][M];

int main()
{
    cin >> n >> m >> k >> a >> b;
    a = " " + a; //使字符串从1开始
    b = " " + b; //使字符串从1开始
    f[0][0][0] = 1;
    for(int i = 1; i <= n; i++)
    {
        f[i][0][0] = 1;
        for(int j = 1; j <= m; j++)
        {
            for(int p = 1; p <= k; p++)
            {
                if(a[i] == b[j])
                {
                    f[i][j][p] = ((f[i-1][j-1][p-1] + g[i-1][j-1][p-1]) % MOD + f[i-1][j-1][p]) % MOD;
                    g[i][j][p] = (f[i-1][j][p] + g[i-1][j][p]) % MOD;
                }
                else
                {
                    f[i][j][p] = 0;
                    g[i][j][p] = (f[i-1][j][p] + g[i-1][j][p]) % MOD;
                }
            }
        }
    }
    cout << (f[n][m][k] + g[n][m][k]) % MOD;
    return 0;
}

再基于70代码进行空间优化

首先,将N、M定义为1005, 205

其次,由于递推关系中i只依赖i-1,可以将f[N][M][M],g[N][M][M]的第一维用滚动数组优化:f[2][M][M],g[2][M][M]

最后,定义now,old变量,每次i枚举时交换位置,达到0/1交替滚动的效果

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1005, M = 205, MOD = 1000000007;
string a, b;
int n, m, k;
int f[2][M][M], g[2][M][M];

int main()
{
    cin >> n >> m >> k >> a >> b;
    a = " " + a; //使字符串从1开始
    b = " " + b; //使字符串从1开始
    f[0][0][0] = f[1][0][0] = 1;
    int now = 0, old = 1; //滚动数组
    for(int i = 1; i <= n; i++)
    {
        swap(now, old); //交替滚动
        for(int j = 1; j <= m; j++)
        {
            for(int p = 1; p <= k; p++)
            {
                if(a[i] == b[j])
                {
                    f[now][j][p] = ((f[old][j-1][p-1] + g[old][j-1][p-1]) % MOD + f[old][j-1][p]) % MOD;
                    g[now][j][p] = (f[old][j][p] + g[old][j][p]) % MOD;
                }
                else
                {
                    f[now][j][p] = 0;
                    g[now][j][p] = (f[old][j][p] + g[old][j][p]) % MOD;
                }
            }
        }
    }
    cout << (f[now][m][k] + g[now][m][k]) % MOD;
    return 0;
}

 

标签:子串,方案,NOIP2015,终点,int,洛谷题,作为,bi,P2679
From: https://www.cnblogs.com/jcwy/p/18160431

相关文章

  • 洛谷题单指南-动态规划2-P1439 【模板】最长公共子序列
    原题链接:https://www.luogu.com.cn/problem/P1439题意解读:求最长公共子序列的长度。解题思路:1、O(n^2)的方法:动态规划设两个排列为a,b设dp[i][j]表示a[1~i]与b[1~j]的最长公共子序列长度根据公共子序列结尾是否包含a[i]、b[j]是否相等分情况讨论:如果a[i]==b[j]  则结尾......
  • 洛谷题解-官方题单-递归与递推
    P1255数楼梯原题链接题目描述楼梯有N阶,上楼可以一步上一阶,也可以一步上二阶。编一个程序,计算共有多少种不同的走法。对于60%的数据,N≤50;对于100%的数据,1≤N≤5000。思路:每次有2种方法上楼梯,要么上一阶,要么上二阶。第一种:得50分的做法是可以用递归来解:点击查看代码......
  • 洛谷题单指南-动态规划2-P2758 编辑距离
    原题链接:https://www.luogu.com.cn/problem/P2758题意解读:对a字符串最少操作几次可以变成b字符串,看似无从下手,可以从内部递推关系着手。解题思路:设dp[i][j]表示将a字符串1~i变成b字符串1~j的最少操作次数,字符串下标从1开始。如何思考递推?可以从最后一步为切入点!最后一步对a[i]......
  • 洛谷题单指南-动态规划2-P1874 快速求和
    原题链接:https://www.luogu.com.cn/problem/P1874题意解读:一个数字字符串s,分解成几个整数,和为n,计算最少加号个数,也就是计算最少分解的整数个数-1。解题思路:此题虽然分类在动态规划,但数据量不大,DFS更加直观和易于理解,所以采用DFS暴搜+剪枝来解决。搜索思路是对数字字符串依次枚......
  • 洛谷题单指南-动态规划2-P4933 大师
    原题链接:https://www.luogu.com.cn/problem/P4933题意解读:求有多少个子序列可以组成等差序列解题思路:1、暴力DFS如果实在想不出动规的方法,对于n<=20的数据,可以DFS枚举所有子序列的子集,再判断是否是等差数列。30分代码:#include<bits/stdc++.h>usingnamespacestd;const......
  • 洛谷题单指南-动态规划2-P1725 琪露诺
    原题链接:https://www.luogu.com.cn/problem/P1725题意解读:走过一系列格子之后,冰冻指数之和最大,相当于计算最大子序列的和。解题思路:设a[0~n]保存所有冰冻指数设dp[i]表示以第i号格子为终点所能获得的最大冰冻指数设j表示i的前一个格子,也就是从j可以移动到i已知i,则j的范围也......
  • 洛谷题单指南-动态规划2-P1020 [NOIP1999 提高组] 导弹拦截
    原题链接:https://www.luogu.com.cn/problem/P1020题意解读:拦截系统发射导弹的高度依次不增,计算能拦截的最大导弹数以及需要几套拦截系统。解题思路:问题1:最多能拦截多少导弹?由于发射导弹高度不增,所以求一个最长不增子序列即可得到最大拦截数。方法一、O(n^2)做法:动态规划。采......
  • 洛谷题单指南-动态规划1-P1064 [NOIP2006 提高组] 金明的预算方案
    原题链接:https://www.luogu.com.cn/problem/P1064题意解读:用固定钱数购买最大价值的物品。解题思路:背包问题,背包问题里的体积相当于物品价格,价值相当于价格*重要度物品分为主件、附件,主件最多有0/1/2个附件,要选附件必须选相应主件,因此在递推计算dp[j]总价格j能购买的最大价......
  • 洛谷题单指南-动态规划1-P3842 [TJOI2007] 线段
    原题链接:https://www.luogu.com.cn/problem/P3842题意解读:计算1-n的最短路,且每行要覆盖线段。解题思路:既然要每行覆盖线段,那往下一行走时,必然是从线段的端点往下,有可能是从左端点往下,也有可能是从右端点往下。当已知第i行,从1走到第i行的左端点且要覆盖第i行线段的路程可以计算......
  • 洛谷题单指南-动态规划1-P1077 [NOIP2012 普及组] 摆花
    原题链接:https://www.luogu.com.cn/problem/P1077题意解读:n种花选m个的选法,每种花数量为ai。解题思路:设dp[i][j]表示前i种花选j个的选法对于第i种花,可以选0,1,2...min(ai,j)个则有递推式:dp[i][j]=∑dp[i-1][j-k],k取0,1,2...min(ai,j)初始化dp[0][0]=1100分代码:#incl......