NOIP2015提高组第二轮day2 - T2:子串

news2024/11/29 9:46:10

题目链接

[NOIP2015 提高组 day2 第二题] 子串

题目描述

有两个仅包含小写英文字母的字符串 A A A B B B

现在要从字符串 A A A 中取出 k k k 个互不重叠的非空子串,然后把这 k k k 个子串按照其在字符串 A A A 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 B B B 相等?

注意:子串取出的位置不同也认为是不同的方案。

输入格式

第一行是三个正整数 n , m , k n,m,k n,m,k,分别表示字符串 A A A 的长度,字符串 B B B 的长度,以及问题描述中所提到的 k k k,每两个整数之间用一个空格隔开。

第二行包含一个长度为 n n n 的字符串,表示字符串 A A A

第三行包含一个长度为 m m m 的字符串,表示字符串 B B B

输出格式

一个整数,表示所求方案数。

由于答案可能很大,所以这里要求输出答案对 1000000007 1000000007 1000000007 取模的结果。

样例 #1

样例输入 #1

6 3 1 
aabaab 
aab

样例输出 #1

2

样例 #2

样例输入 #2

6 3 2 
aabaab 
aab

样例输出 #2

7

样例 #3

样例输入 #3

6 3 3 
aabaab 
aab

样例输出 #3

7

提示

对于第 1 组数据: 1 ≤ n ≤ 500 , 1 ≤ m ≤ 50 , k = 1 1≤n≤500,1≤m≤50,k=1 1n500,1m50,k=1;
对于第 2 组至第 3 组数据: 1 ≤ n ≤ 500 , 1 ≤ m ≤ 50 , k = 2 1≤n≤500,1≤m≤50,k=2 1n500,1m50,k=2;
对于第 4 组至第 5 组数据: 1 ≤ n ≤ 500 , 1 ≤ m ≤ 50 , k = m 1≤n≤500,1≤m≤50,k=m 1n500,1m50,k=m;
对于第 1 组至第 7 组数据: 1 ≤ n ≤ 500 , 1 ≤ m ≤ 50 , 1 ≤ k ≤ m 1≤n≤500,1≤m≤50,1≤k≤m 1n500,1m50,1km;
对于第 1 组至第 9 组数据: 1 ≤ n ≤ 1000 , 1 ≤ m ≤ 100 , 1 ≤ k ≤ m 1≤n≤1000,1≤m≤100,1≤k≤m 1n1000,1m100,1km;
对于所有 10 组数据: 1 ≤ n ≤ 1000 , 1 ≤ m ≤ 200 , 1 ≤ k ≤ m 1≤n≤1000,1≤m≤200,1≤k≤m 1n1000,1m200,1km

算法思想(朴素版动态规划,70分)

  • 状态表示:f[i][j][k]表示从字符串 A A A 的前i个字符中取出 k 个互不重叠的非空子串,组成的新串与字符串 B B B的前j个字符相等的方案数。

  • 状态计算,从最后一步分析,对于字符串 A A A 的第i个字符可以选择用或者不用,分为下面2种情况:

    • 不使用第i个字符,方案数为:f[i-1][j][k]
    • 使用第i个字符,那么第i个字符可以作为第k个子串的结尾,那么该子串的长度可以为 1 , 2 , . . . , j 1,2,...,j 1,2,...,j,又可以分为 j j j种情况:
      • k个子串长度为 1 1 1,方案数为f[i-1][j-1][k-1]
      • k个子串长度为 2 2 2,方案数为f[i-2][j-2][k-1]
      • k个子串长度为 t t t,方案数为f[i-t][j-t][k-1] t ≤ j t\le j tj
      • k个子串长度为 j j j,方案数为f[i-j][0][k-1]

      注意:上述情况的方案存在的前提是 A A A的子串 A [ i − t + 1... i ] A[i -t+1...i] A[it+1...i] B B B的子串 B [ j − t + 1... j ] B[j-t+1...j] B[jt+1...j]是相等的,否则对应情况的方案数为 0 0 0

    因此f[i][j][k]的方案总数为:f[i][j][k] = f[i-1][j][k] + (f[i-1][j-1][k-1] + f[i-2][j-2][k-1]+...+f[i-j][0][k-1])

  • 初始状态:f[0][0][0] = 1

时间复杂度

状态数为 n × m × k n\times m\times k n×m×k,其中 k ≤ m k\le m km,因此状态数为 O ( n m 2 ) O(nm^2) O(nm2);状态计算的次数为 k k k,因此时间复杂度为 O ( n m 3 ) = 1000 × 20 0 3 = 8 , 000 , 000 , 000 O(nm^3)=1000\times200^3=8,000,000,000 O(nm3)=1000×2003=8,000,000,000

代码实现

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010, M = 210, MOD = 1e9 + 7;
int f[N][M][M];
char a[N], b[M];

int main()
{
    int n, m, K;
    cin >> n >> m >> K;
    scanf("%s%s", a + 1, b + 1);
    f[0][0][0] = 1;
    for(int i = 1; i <= n; i ++) //枚举字符串a的每个位置	
        for(int j = 0; j <= m; j ++) //枚举字符串b的每个位置	
            for(int k = 0; k <= K; k ++) //枚举k个互不重叠的非空子串
            {
                int sum = 0; //计算使用第i个字符情况下的方案总数
                //枚举第k个子串的长度t
                for(int t = 1; t <= j; t ++)
                {
                    if(a[i - t + 1] != b[j - t + 1]) break; //如果子串不相等,则接下来的方案数都为0
                    sum = (sum + f[i - t][j - t][k - 1]) % MOD; //累加不同情况的方案数
                }
                f[i][j][k] =(f[i - 1][j][k] + sum) % MOD; //计算不使用第i个字符和不是用第i个字符的方案总数
            }
    cout << f[n][m][K]; 
    return 0;
}

时空优化(100分)

  • 首先考虑时间复杂度的优化。

根据上述分析,状态转移方程f[i][j][k] = f[i-1][j][k] + (f[i-1][j-1][k-1] + f[i-2][j-2][k-1] + ... + f[i-j][0][k-1]),不妨设sum[i][j][k] = f[i-1][j-1][k-1] + f[i-2][j-2][k-1] + ... + f[i-j][0][k-1],那么f[i][j][k] = f[i-1][j][k] + sum[i][j][k];而sum[i][j][k]根据 A [ i ] A[i] A[i] B [ j ] B[j] B[j]是否相同可以分为 2 2 2类:

  • A [ i ] ≠ B [ j ] A[i] \ne B[j] A[i]=B[j]时,那么以 A [ i ] A[i] A[i]作为结尾的子串方案数为 0 0 0,即sum[i][j][k] = 0
  • A [ i ] = B [ j ] A[i] = B[j] A[i]=B[j]时,sum[i][j][k] = f[i-1][j-1][k-1] + sum[i-1][j-1][k],其中sum[i-1][j-1][k] = f[i-2][j-2][k-1] + ... + f[i-j][0][k-1]

因此可以通过递推得到sum[i][j][k],从而将状态计算的时间复杂度降为 O ( 1 ) O(1) O(1),总的时间复杂度变为 O ( n m 2 ) = 1000 × 20 0 2 = 40 , 000 , 000 O(nm^2)=1000\times200^2=40,000,000 O(nm2)=1000×2002=40,000,000

  • 其次考虑空间复杂度的优化

仔细分析转移方程f[i][j][k] = f[i-1][j][k] + sum[i][j][k],发现f[i][j][k]只与i - 1阶段的状态有关。因此可以使用滚动数组进行优化。同时可以发现jk只会从更小的值转移过来,因此可以使用类似于01背包问题优化空间的方式,从大到小枚举jk。这样可以直接忽略状态中的第一维,空间复杂度变为 O ( m × k ) = 20 0 2 = 40 , 000 O(m\times k)=200^2=40,000 O(m×k)=2002=40,000

代码实现

滚动数组

#include <iostream>
using namespace std;
const int N = 1010, M = 210, MOD = 1e9 + 7;
int f[2][M][M], sum[2][M][M];
char a[N], b[M];
int main()
{
    int n, m, K;
    cin >> n >> m >> K;
    scanf("%s%s", a + 1, b + 1);
    f[0][0][0] = 1;
    for(int i = 1; i <= n; i ++)
        for(int j = 0; j <= m; j ++)
            for(int k = 0; k <= K; k ++)
            {
                if(a[i] != b[j]) sum[i & 1][j][k] = 0; //以a[i]结尾的子串的方案数为0
                else 
                {
                    if(j > 0) //存在该状态
                    {
                    	//递推求sum[i][j][k]
                        sum[i & 1][j][k] = sum[i & 1][j - 1][k]; 
                        if(k > 0) sum[i & 1][j][k] = (f[i - 1 & 1][j - 1][k - 1] + sum[i - 1 & 1][j - 1][k]) % MOD; 
                    }
                }
                f[i & 1][j][k] = (f[i - 1 & 1][j][k] + sum[i & 1][j][k]) % MOD;
            }
    cout << f[n & 1][m][K];
    return 0;
}

空间优化

#include <iostream>
using namespace std;
const int N = 1010, M = 210, MOD = 1e9 + 7;
int f[M][M], sum[M][M];
char a[N], b[M];
int main()
{
    int n, m, K;
    cin >> n >> m >> K;
    scanf("%s%s", a + 1, b + 1);
    f[0][0] = 1;
    for(int i = 1; i <= n; i ++)
        for(int j = m; j >= 0; j --)
            for(int k = K; k >= 0; k --)
            {
                if(a[i] != b[j]) sum[j][k] = 0; //以a[i]结尾的子串的方案数为0
                else sum[j][k] = (f[j - 1][k - 1] + sum[j - 1][k]) % MOD; 
                f[j][k] = (f[j][k] + sum[j][k]) % MOD; //递推求sum[i][j][k]
            }
    cout << f[m][K];
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1187643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科技改变农业:合成数据农业中的应用

介绍 农业在我们的生活中起着至关重要的作用&#xff0c;它为我们提供了生存的食物。如今&#xff0c;它遇到了各种困难&#xff0c;例如气候变化的影响、缺乏工人以及全球流行病造成的中断。这些困难影响了耕作用水和土地的供应&#xff0c;而这些水和土地正变得越来越稀缺。…

Python基础教程之六:Python中的关键字

Python关键字是python编程语言的保留字。这些关键字不能用于其他目的。 Python中有35个关键字-下面列出了它们的用法。 KeywordDescriptionandA logical AND operator. Return True if both statements are True. x (5 > 3 and 5 < 10) print(x) # True orA logic…

率能SS6216-单通道直流有刷电机驱动芯片

产品描述&#xff1a; SS6216是一款单通道直流有刷驱动芯片&#xff1b;工作电压为 2.0V&#xff5e;7.2V&#xff0c;每个通道的负载电流可达1.4A;峰值输出电流1.6A&#xff1b;低待机电流 (typ. 0.1uA&#xff09;低导通电阻0.6ohm(采用SOP8/SOT23-6两种封装)满足产品小型化…

pytest + yaml 框架 -58.运行报告总结summary.json

前言 用例运行结束后&#xff0c;在本地生成summary.json 文件&#xff0c;总结运行结果。 v1.5.1版本更新内容&#xff1a; 1.解决参数化&#xff0c;中文在控制台输出问题 2.保存用例结果summary.json 保存用例结果summary.json 命令行执行用例 pytest运行结束&#xff0…

查找-树表的查找-平衡二叉树

目录 平衡二叉树得定义插入操作平衡二叉树的平衡调整方法查找效率分析 平衡二叉树得定义 平衡二叉树(Balanced Binary Tree),简称平衡树(AVL树)&#xff0c;平衡二叉树或者空树&#xff0c;或者是具有以下特征得二叉树排序是&#xff1a; 左子树与右子树得深度之差得绝对值不超…

柯桥英语培训,商务英语学习,常用口语

欢迎各位小伙伴来到 ——“每个单词我都认识&#xff0c;但我又不认识整个短语”的时候啦&#xff01; “dog”是“狗” “breakfast”是早餐 那“a dogs breakfast”是“狗的早餐”&#xff1f; 狗听了都摇头。 a dogs breakfast是一句英文俚语&#xff0c;指的是无序、混…

2010年09月15日 Go生态洞察:探索Go Playground的新颖之处

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

异地传输大文件最快且安全稳定的办法

无论是企业还是个人&#xff0c;都会有传输大文件的需求&#xff0c;特别是在异地时&#xff0c;工作中最典型的就是项目资料、合同文档、视频素材等都是有一定的及时性的&#xff0c;那么在传输过程中&#xff0c;没有好的传输方式会间接性的影响到整体工作的进行&#xff0c;…

假脱机技术

一、脱机技术 二、引入假脱机技术 1.相关概念抽象 2.总体结构 三、实现过程

撕开AEB的「遮羞布」

如果&#xff0c;以NOA为代表的高阶智驾&#xff0c;考量的更多是在车辆运动条件下&#xff0c;如何更好的规避障碍物&#xff0c;并实现平稳的驾乘体验&#xff1b;那么&#xff0c;以AEB代表的主动安全功能&#xff0c;则需要更多考量「安全」刹停。 根据此前公安部发布的公开…

【ASP.NET】检验科实验室信息管理系统源码

LIS是全院信息化建设的一个重要组成部分&#xff0c;其主要功能是将检验的实验仪器传出的检验数据经分析后&#xff0c;生成检验报告&#xff0c;通过网络存储在数据库中&#xff0c;使医生能够方便、及时的看到患者的检验结果&#xff0c;LIS已经成为现代化医院管理中必不可少…

【h5 uniapp】 滚动 滚动条,数据跟着变化

uniapp项目 需求&#xff1a; 向下滑动时&#xff0c;数据增加&#xff0c;上方的日历标题日期也跟着变化 向上滑动时&#xff0c;上方的日历标题日期跟着变化 实现思路&#xff1a; 初次加载目前月份的数据 以及下个月的数据 this.getdate()触底加载 下个月份的数据 onReach…

17 _ 跳表:为什么Redis一定要用跳表来实现有序集合?

上两节我们讲了二分查找算法。当时我讲到,因为二分查找底层依赖的是数组随机访问的特性,所以只能用数组来实现。如果数据存储在链表中,就真的没法用二分查找算法了吗? 实际上,我们只需要对链表稍加改造,就可以支持类似“二分”的查找算法。我们把改造之后的数据结构叫做…

专业英国TOP1|设计学老师CSC公派伯恩茅斯大学访学

F老师的研究方向侧重于数字设计&#xff0c;比较小众&#xff0c;英国知名大学中涉及该专业的院系不是很多&#xff0c;且只有一个多月的申请时间。我们的申请效率很高&#xff0c;陆续得到英国多个高校的邀请函&#xff0c;最终其选定了伯恩茅斯大学申报CSC。伯恩茅斯大学的动…

对话刘继升:用户只管去“野”,剩下的交给福特纵横

在云南首家Ford Beyond福特纵横纵享空间的盛大开业典礼上&#xff0c;福特再次加速了其福特纵横网络建设&#xff0c;为越野爱好者带来更多乐趣。这一举措标志着福特正积极构筑一个全新的越野生态系统&#xff0c;为越野爱好者提供更多愉快的体验&#xff0c;同时打造一个充满乐…

Webpack--动态 import 原理及源码分析

前言 在平时的开发中&#xff0c;我们经常使用 import()实现代码分割和懒加载。在低版本的浏览器中并不支持动态 import()&#xff0c;那 webpack 是如何实现 import() polyfill 的&#xff1f; 原理分析 我们先来看看下面的 demo function component() {const btn docume…

5个WebGIS功能小技巧

我们在《为什么要研发WebGIS系统&#xff1f;》一文中&#xff0c;分享为什么要研发水经微图Web版的WebGIS系统。 这里&#xff0c;我们再为你分享一下水经微图Web版中的几个功能小技巧。 批量修改标注名称 在工具栏中选择“框选”工具&#xff0c;框选需要修改标注的要素。 …

自定义表单模型小程序源码系统 带完整的部署教程

大家好啊&#xff0c;今天源码小编来给大家分享一款自定义表单模型小程序源码系统。在数字化时代&#xff0c;信息收集和处理显得尤为重要。无论是企业还是个人&#xff0c;都需要通过表单来收集、整理、分析各种信息。但是&#xff0c;传统的表单构建方式往往需要编写大量的代…

电脑如何截屏?一起来揭晓答案!

在数字时代&#xff0c;截屏已经成为我们日常生活和工作中的必备技能。无论是为了捕捉有趣的网络瞬间&#xff0c;保存重要信息&#xff0c;还是为了协作和教育&#xff0c;电脑截屏都是一个强大而方便的工具。本文将介绍三种电脑如何截屏的方法&#xff0c;以满足各种需求&…

景联文科技助力金融机构强化身份验证,提供高质量人像采集服务

随着社会的数字化和智能化进程的加速&#xff0c;人像采集在金融机构身份认证领域中发挥重要作用&#xff0c;为人们的生活带来更多便利和安全保障。 金融机构在身份验证上的痛点主要包括以下方面&#xff1a; 身份盗用和欺诈风险&#xff1a;传统身份验证方式可能存在漏洞&am…