luogu P2679——子串
如题,我们分析一下。好像是一个dp???
对啊好像是。
那么想一下状态;
dp[ i ][ j ][ k ]表示a串匹配到i的位置,b串匹配到j的位置,用了k个子串的方案数;
当a[i]==b[j]时,则有两种情况:
一是可以自己成为一个子串:dp[i-1][j-1][k-1];
二是和前一个凑成一个子串:dp[i-1][j-1][k];
那么转移方程就为
dp[ i ][ j ][ k ]=(dp[i-1][j-1][k-1]+dp[i-1][j-1][k])%mod;
可是好像有一些问题,就是这一个位置就算他们相等也可以不选,跳过;
那么我们就要重设状态了;
dp[ i ][ j ][ k ][0/1]表示当前这个字符选(0)/选不选无所谓( 1 )的方案数;
但是我们看一下数据范围1000*200*200*2*4(int的字节)/1024/1024=305.17578125>128;
肯定是开不下的,那我们尝试把第一维抹掉;
dp[ j ][ k ][0]表示b字符串中匹配到j这个位置,用了k个子串,并且当前这个位置一定选的方案数;
dp[ j ][ k ][1]表示b字符串中匹配到j这个位置,用了k个子串,并且当前这个位置不一定选的方案数;
当a[i]==b[j]时,dp[ j ][ k ][0]仍旧有两种转移
一是可以自己成为一个子串,不管前面一个选或不选:dp[j-1][k-1][1];
二是和前一个凑成一个子串:dp[j-1][k][0];
那么dp[ j ][ k ][1]的转移为
一当前这一位取:dp[ j ][ k ][0];
二当前这一位不取:dp[ j ][ k ][1];
如果a[ i ]!=a[ j ]
那么当前b里这一位选的方案数肯定是0:dp[j][k][0]=0;
最后只需要输出dp[b字符串的长度][子串总数][1]即可,因为最后一位可取可不取;
既然是dp方程,肯定要有初值的啊
dp[0][0][0]=dp[0][0][1]=1;(某神犇传授的经验,一般求方案数的dp,边界条件都是1)
然后就结束了;
感谢叶犇犇的指导;
贴一发 ↑ 神犇的博客
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> using namespace std; const int mod=1000000007; char a[1200],b[210]; int n,m,tot; int dp[210][210][3]; int main() {scanf("%d%d%d",&n,&m,&tot);cin>>a+1;cin>>b+1;/*for(int i=1;i<=n;i++)cout<<a[i]<<" ";cout<<endl;for(int i=1;i<=m;i++)cout<<b[i]<<" ";*/dp[0][0][0]=dp[0][0][1]=1;//第三维是零表示这一位必选,是1表示选不选都行; for(int i=1;i<=n;i++)for(int j=m;j>=1;j--)for(int k=1;k<=tot;k++){if(a[i]==b[j]){dp[j][k][0]=(dp[j-1][k][0]+dp[j-1][k-1][1])%mod;//独自成一串(dp[j-1][k-1][1])或者是与前面成一串 ( dp[j-1][k][0]); dp[j][k][1]=(dp[j][k][0]+dp[j][k][1])%mod;//当前这一位取的话就是dp[j][k][0],不取的话就是dp[j][k][1](这其实是从上一位转移过来的); }elsedp[j][k][0]=0; }printf("%d",dp[m][tot][1]);return 0; } 标程转载注明来源,谢谢;
转载于:https://www.cnblogs.com/royal-8/p/9084860.html
总结
以上是生活随笔为你收集整理的luogu P2679——子串的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 【MVC】Controller的使用
- 下一篇: kafka shell