2019牛客暑期多校训练营(第五场)G subsequence 1(dp+组合数)
题目链接:https://ac.nowcoder.com/acm/contest/885/G
题目大意:给一个s串和t串,问s中任意子串(视为正整数),大于t字符串(视为正整数)的总个数。
例如:s:1234, t:13,答案共有9个。
为14,23,24,34,123,124,134,234,1234。
解题报告:
1.先求s串中长度与t串相等的子字符串。开一个三维数组,第一维:s串的长度,第二维:t串的长度,第三维:当s串长为i,t串长为j时,0代表前面每一位都相等的方案数,1代表此时s串已经大于t串的方案数。然后就可以开始写转移方程啦。
2.接着要求出s串中长度比t串长的子字符串(需要用到组合数,可以通过打表解决),即求长为n的字符串中长度大于m的子字符串数量,可以先求当n=5,里面长为3的子字符串个数,会发现是C(5,3),有10个,从m+1枚举到n便可解决。
3.排除前导零的情况,可以发现,当前位为0时,求的是这一位后面的字符串中长度大于m的字符串,设后面的长度(不包括0这一位)为len,便是C(len,m),从m枚举到n便可解决。
AC代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<vector> 2 #include<cstdio> 3 #include<iostream> 4 #include<cmath> 5 #include<queue> 6 #include<stack> 7 #include<cmath> 8 #include<algorithm> 9 #define numm ch-48 10 #define pd putchar(' ') 11 #define pn putchar('\n') 12 #define pb push_back 13 #define fi first 14 #define se second 15 #define fre1 freopen("1.txt","r",stdin) 16 #define fre2 freopen("2.txt","w",stdout) 17 using namespace std; 18 template <typename T> 19 void read(T &res) { 20 bool flag=false;char ch; 21 while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true); 22 for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm); 23 flag&&(res=-res); 24 } 25 template <typename T> 26 void write(T x) { 27 if(x<0) putchar('-'),x=-x; 28 if(x>9) write(x/10); 29 putchar(x%10+'0'); 30 } 31 const int maxn=20010; 32 const int N=60; 33 const int inf=0x3f3f3f3f; 34 const int INF=0x7fffffff; 35 const int mod=998244353; 36 typedef long long ll; 37 char s[3010],t[3010]; 38 ll c[3010][3010]; 39 int n,m; 40 int dp[3010][3010][2]; 41 void init() { 42 c[0][0]=1; 43 for(int i=1;i<=3000;i++) { 44 c[i][0]=1; 45 for(int j=1;j<=i;j++) 46 c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; 47 } 48 } 49 ll DP() { 50 for(int i=1;i<=n;i++) { 51 for(int j=1;j<=m;j++) { 52 dp[i][j][0]=dp[i-1][j][0]; ///代表相等的情况 53 dp[i][j][1]=dp[i-1][j][1]; ///代表大于的情况 54 if(j-1==0) { 55 if(s[i-1]>t[j-1]) ///首字符就开始大于 56 dp[i][j][1]=(1LL+dp[i][j][1])%mod; 57 } 58 else { 59 if(s[i-1]>t[j-1]) ///子串现在才s大于t 60 dp[i][j][1]=(dp[i-1][j-1][0]+dp[i][j][1])%mod; 61 dp[i][j][1]=(dp[i-1][j-1][1]+dp[i][j][1])%mod;///前面就已经是大于的关系 62 } 63 if(s[i-1]==t[j-1]) { 64 if(j-1==0) 65 dp[i][j][0]=(1LL+dp[i][j][0])%mod; 66 else dp[i][j][0]=(dp[i-1][j-1][0]+dp[i][j][0])%mod; 67 } 68 } 69 } 70 return dp[n][m][1]%mod; 71 } 72 73 int main() 74 { 75 int _; 76 read(_); 77 init(); 78 while(_--) { 79 read(n);read(m); 80 scanf("%s%s",s,t); 81 for(int i=0;i<=m;i++) 82 for(int j=0;j<=n;j++) 83 dp[i][j][0]=dp[i][j][1]=0; 84 ll ans=DP(); ///1.先算s中和t长度相等的子字符串数量 85 // write(ans);pn; 86 for(int i=m+1;i<=n;i++) ///2.加上长度为n的字符串中,长度大于等于m+1的子字符串数量 87 ans=(ans+c[n][i])%mod; 88 89 for(int i=0;i<=n;i++) ///3.减掉以0开头的子字符串 90 if(s[i]=='0') 91 for(int j=m;j<=n-i-1;j++) 92 ans=(ans-c[n-i-1][j]+mod)%mod; 93 94 write(ans);pn; 95 } 96 return 0; 97 }
所谓人生,一半惊喜,一半遗憾