String Game(子串个数+DP求解)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

关于本题,只是自己关于DP的一点小小见解,如有问题,欢迎在评论区发表你尖锐的看法。

如果有更好的方法,欢饮在评论区讨论。

一、String Game题目描述

Clair和Bod在玩一个游戏。克莱尔写了一串小写字母,鲍勃在其中设置了这个谜题
通过选择一个他最喜欢的子序列作为游戏的关键词。但是鲍勃太笨了,他可能会写错一些字母。(可能存在这个子序列在Clair写的小写字母中没有出现)
现在Clair想知道Bod的字符串是否是克莱尔的字符串的子序列以及有多少次
Bob的字符串是否作为子序列出现在克莱尔的字符串中。因为答案可能很大,你应该
以10^9 + 7模输出答案。

(本题的 mod = 1000000007)

输入:

第一行是claire的字符串(长度不超过5000),第二行是Bob的字符串(长度不超过1000)。

输出:
输出一行,包括一个整数,表示Bob的字符串作为a出现的次数
Clair字符串中的子序列。答案应该对10^9+ 7取模。

样例1:

eeettt

et

结果: 9

样例2:

eeettt

te

结果

0

二、分析步骤

1.分析

        我们一拿到这道题,这啥呀这是?

        这是一道典型的动态规划题,当听到动态规划,当时的我已经慌了,真不知道怎么办。

但是DP的基本步骤是:

        什么找最优子结构(大问题得到最好解,那么大问题的部分问题也是最好解决方案,如果存在更好解决方案,那么大问题得到更好解,但前提是大问题已经是最好解,你的部分问题的方案肯定是最好解了,如果存在更好,大问题会说,你礼貌吗?)

        什么状态转移方程(这步虽难,但也是有鸡可循(哎哟,你干嘛),根据我的理解,是要从顶上往下看,这里的顶就是所要求解问题的答案,我们下面对这个展开)

        其实分析部分上面没看懂,问题不大,但从这里开始就有点东西啦。

我们就题论题,再从本题总结规律(我们遵循马克思主义理论)

        OK,我们看到我们要求解的问题,Clair的字符串是更长的,我们用n表示Clair字符串的长度

用m表示Bob字符串的长度

n = strlen(Clair)        m = strlen(Bob)

我们定义一个数组dp[n][m],为什么名字叫dp,因为DP(Dynamic Programming,动态规划),所以DP

为什么dp后面跟着[n][m]因为n表是Clair字符串的长度,m表示Bob字符串的长度

题目要我们求Clair字符串中有多少个像Bob字符串的个数,即子串个数.

那我们就赋予dp[n][m]表示的是这个答案(即在长度为n的字符串里,出现长度为m的字符串,并且后面长度为m的字符串是前面长度为n的字符串的子串)

dp[n][m],一拿到,什么鬼?

但是不要着急,这里面是有条件的,比如它会给你Clair和Bob的字符串,没有他们的字符串,

哪来这个问题,哪来n和m呢?

因此,我们第一步是要将他们的字符串存起来,才有后面的东西(n,m,dp[n][m],以及这个题的答案)

关于一个算法题,我会把它抽象成如下形式:

输入就是要存东西,核心算法就是处理,输出就是printf

 

我们看到输入样例1:

eeettt     et

dp[m][n]表示Clair前m个字符串有Bob前n个字符串这个子串的个数

此时我们要找递推方程,怎么找,一般找邻近的dp[m-1][n],dp[m-1][n-1],dp[m][n-1]

dp[m-1][n]:Clair前m-1个字符串有Bob前n个字符串这个子串的个数

dp[m-1][n-1]:Clair前m-1个字符串有Bob前n-1个字符串这个子串的个数

dp[m][n-1]:Clair前m个字符串有Bob前n-1个字符串这个子串的个数

Clari字符串简称为C串, Bob字符串简称为B串

Clari字符串前m个字符串简称为C(m)

Bob字符串前n个字符串简称为B(n)

当要求dp[m][n]时,   还要依据s[m]与t[n]的关系

s[m]==t[n] 时

dp[m][n]=dp[m-1][n]+dp[m-1][n-1]

我又把它理解成下面那个式子

dp[m][n] = dp[m-1][n] + dp[m-1][n-1]~t[n]

因为C(m-1)已经含有B(n),也是C(m)的一部分

至于C(m-1)含有B(n-1)时,也要加上,因为s[m]=t[n]

~表示在C(m-1)找到B(n-1)的个数后,连接t[n]后就是C(m)含有B(n)的个数

 

我感觉写的差不多了,如果你们觉得哪里还要进一步补充,或者可以进一步优化让大家更好理解,请在评论区留言。如有错误,请指正。

 

2.代码实现

当然,我就知道你喜欢跳到这个地方(^_^),无所谓,我会出手(写注释.)

代码如下:

//String Game(DP + 子串个数)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef long long ll;	//给long long 一个简称ll 

const int mod = 1e9 + 7;
int dp[5005][1005];		//全局变量,自动初始化为0 
char s[5005];//用来存储Clair字符串
char t[1005];//用来存储Bob字符串 
//Clair's String length <=5000
//Bob's String length <=1000
//所以上面的dp数组前一个大小为5000左右
//后一个大小为1000左右 
//dp[m][n] 表示Clair前m个字符串含Bob前n个字符串的个数 

//eg:
//Clair: eeettt   (length=6)
//Bob:   et		  (length=2)
//dp[3][1] = 3 Clair前3个字符串(eee)含有Bob前一个字符串e的个数为3
//再来几个
//dp[1][1] = 1 dp[2][1] = 2 dp[3][1] = 3
//dp[4][1] = 3 dp[5][1] = 3 dp[6][1] = 3
//dp[1][2] = 0 dp[2][2] = 0 dp[3][2] = 0
//dp[4][2] = 3 dp[5][2] = 6 dp[6][2] = 9

//我们要先找到递推方程
//第一步: 明白dp[m][n]的含义,Clair前m个字符串含Bob前n个字符串的个数
//第二步: dp[m][n] = dp[m-1][n](前面含有n的要加上)  +  dp[m-1][n-1](前面m-1含有n-1个它们再各自连接s
//[m]和t[n]也可以得到前m个Clair含有前n个Bob) 
//递推方程    dp[m][n]  =  dp[m-1][n] + dp[m-1][n-1] 
int main()
{
	gets(s);//gets()获取一行字符串,不包括回车
	gets(t);
	
	int m = (int)strlen(s);
	int n = (int)strlen(t);
	//printf("m = %d\n",m);
	//printf("n = %d\n",n);
	
	//利用char s[m] 和 char t[n]更新求解dp[m][n]
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(s[i-1]==t[j-1]) //s的第i个字符在数组里是s[i-1],s[0]是第一个字符 
			{
				 if(j==1)	//如果是第一列,直接+1 
				 {
				 	dp[i][1] = dp[i-1][1] + 1;
				 }
				 else
				 {
				 	dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
				 }
			}
			else
			{
				dp[i][j] = dp[i-1][j];
			}
			dp[i][j] %= mod;//数据过大要mod 
		}
	}
	
	printf("%d",dp[m][n]);
	return 0;
}

运行结果:

 完结撒花(好耶!)

如果代码哪里有误,或则哪里可以改进,都可以写评论,动态规划给我的感觉就好像痛苦面具。

但是学会后,他就如同神力般助你如何思考万千变化世界的某些问题。


总结

最好的总结就是我写了总结。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值