题解 P2679 【子串】

八重樱飞

2019-10-07 15:54:35

Solution

# 蒟蒻表示研究DP方程很久的说 尽管各位大佬都觉得此题DP方程很简单 但是可能也会有像我这样的蒟蒻看的不是很懂 于是在蒟蒻研究了很久之后,终于AC了 之后蒟蒻将会从动规的三要素讲起 ## 关于阶段 由题可知,阶段应如此划分(~~蒟蒻认为~~) A串的第i位 匹配到了B串第j位 使用了p个子串 (应该可以理解吧) 至于为什么?咱们看看题目 从A串中取出k个互不重叠的非空子串,把这k个子串按照出现的顺序依次连接起来得到一个新的字符串,并使得这个新串与B串相等 嗯嗯嗯,我相信各位都懂了吧 ## 关于状态 状态一: A串第i位字符 状态二: B串第j位字符 状态三: 当前使用的子串数p(注意p<=k) 以及 状态四:a[i]是否能匹配得上b[j] 还有状态五,但是这个稍作悬念,下一栏再进一步分析 ## 关于决策 但是由于状态四的不确定性,得分两种情况 1.a[i]=b[j]时 决策一:使用该字符匹配 决策二:不使用该字符匹配 2.a[i]!=b[j]时 只能不使用该字符匹配 由此可见,状态五便华丽丽地登场了 那就是 是否会使用该字符 我们可以用0表示不使用,1表示使用 ## 综上所述 得出动态转移方程 1.a[i]=b[j]时 决策一:f[i][j][p][1]=f[i-1][j-1][p][1]+f[i-1][j-1][p-1][0]+f[i-1][j-1][p-1][1] (~~代码里也有解释,不过不是很详尽~~) 对于上述的数组解释如下 f[i][j][p][1]:A串前i个使用了k个子串匹配到B串前j个,并且使用了当前a[i]的种数 f[i-1][j-1][p][1]:将当前字符纳入前一个子串,并使用前一个字符的种数 f[i-1][j-1][p-1][0]:将当前字符纳入新子串,但不使用前一个字符的种数 f[i-1][j-1][p-1][1]:将当前字符纳入新子串,并使用前一个字符的种数 对于上述方程的解释: 将该字符纳入前一个子串算一种情况(因为纳入前一个子串,故肯定是使用) 将该字符单独看成一个新子串算一种,而看成一个新子串,又分两种:第一种是不使用前一个字符,第二种是不使用前一个字符。(因为是新子串,所以前一个字符与当前字符是独立的,所以有两种) 决策二:f[i][j][p][0]=f[i-1][j][p][1]+f[i-1][j][p][0] 未使用该字符,故B子串匹配数仍为j,子串仍为k. 未使用该字符的种数即前一个字符使用的种数+前一个字符未使用的种数(继承上一阶段的) 2.a[i]!=b[j]时 f[i][j][p][1]=0 不能使用该字符,所以为0 f[i][j][p][0]=f[i-1][j][p][1]+f[i-1][j][p][0]; 因为不能使用该字符,所以便继承上一阶段的值 ## 提醒 数组 1000 * 200 * 200 * 2肯定会爆,故滚动第一维(因为第一维只有i和i-1) 最后答案应该为f[n][m][k][1]+f[n][m][k][0]相加 (最后一个字符使用和不使用是两种情况嘛,最终答案应该是他们之和) ## 上代码 于是,各位最爱的代码上线了 ```cpp #include<iostream> #include<cstdio> using namespace std; int md=1000000007; int n,m,k,f[2][222][222][2]; char a[1001],b[201]; int main() { int i,j,p; scanf("%d%d%d",&n,&m,&k); for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=m;i++) cin>>b[i]; f[0][0][0][0]=1; f[1][0][0][0]=1;//初始化,否则之后都得是0 for(i=1;i<=n;i++) for(j=1;j<=m;j++) for(p=1;p<=k;p++) { if(a[i]==b[j]) { f[i%2][j][p][1]=(f[(i-1)%2][j-1][p][1]+f[(i-1)%2][j-1][p-1][0])%md+f[(i-1)%2][j-1][p-1][1]%md; //f[i-1][j-1][p][1]:将当前字符纳入前一个子串,并使用前一个字符的种数 //f[i-1][j-1][p-1][0/1]:将当前字符纳入新子串,但不使用(使用)前一个字符的种数 f[i%2][j][p][1]%=md; f[i%2][j][p][0]=f[(i-1)%2][j][p][1]+f[(i-1)%2][j][p][0]; //未使用该字符的种数即前一个字符使用的种数+前一个字符未使用的种数 f[i%2][j][p][0]%=md; } else { f[i%2][j][p][0]=f[(i-1)%2][j][p][1]+f[(i-1)%2][j][p][0]; f[i%2][j][p][0]%=md; f[i%2][j][p][1]=0;//不能使用该字符 } } printf("%d",(f[n%2][m][k][1]+f[n%2][m][k][0])%md); return 0; } ```