CF611D 【分割字符串使得形成的数字数组呈递增状态】的方法数
2016-02-12 22:38
573 查看
这题比较巧妙的是运用最长公共前缀长度简化问题以及运用sum数组缩减复杂度。注意要求分割后所有数字不得出现前导零。
题解:点击打开链接
没用sum数组的超时代码:
AC代码:
情况里可以出现前导零,即没有考虑到【要求分割后所有数字不得出现前导零】的代码(读漏题目的后果):
题解:点击打开链接
没用sum数组的超时代码:
#include<bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; int lcp[5001][5001]; ll dp[5001][5001]; int main(){ int n; cin>>n; string x; cin>>x; x='0'+x; for(int i=n;i>=1;--i){ for(int j=n;j>i;--j){ if(x[i]==x[j]) lcp[i][j]=lcp[i+1][j+1]+1; } } for(int i=1;i<=n;++i) dp[0][i]=1; for(int i=1;i<=n;++i){ //这个子串的结尾位置 for(int j=1;j<=i;++j){ //dp[i][j]:以【从x[i]往前j长度的子串】结尾的可能数 int p=i-j+1; //这个子串的开头位置 if(x[p]=='0') continue; for(int k=p-1;k>=max(p-j,0);--k){ //枚举上一个子串的开头位置(上一个一定不能比这个长) char a=x[k+lcp[k][p]],b=x[p+lcp[k][p]]; if(k!=p-j||(lcp[k][p]<j&&a<b)) dp[i][j]=(dp[i][j]+dp[p-1][p-k])%mod; } } } ll s=0; for(int i=1;i<=n;++i){ s=(s+dp [i])%mod; } cout<<s<<endl; return 0; }
AC代码:
#include<bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; int lcp[5001][5001]; ll dp[5001][5001]; ll sum[5001][5001]; int main(){ int n; cin>>n; string x; cin>>x; x='0'+x; for(int i=n;i>=1;--i){ for(int j=n;j>i;--j){ if(x[i]==x[j]) lcp[i][j]=lcp[i+1][j+1]+1; } } for(int i=0;i<=n;++i) sum[0][i]=1; for(int i=1;i<=n;++i){ //这个子串的结尾位置 for(int j=1;j<=i;++j){ //dp[i][j]:以【从x[i]往前j长度的子串】结尾的方法数 int p=i-j+1; //这个子串的开头位置 if(x[p]=='0') continue; // for(int k=p-1;k>=max(p-j,0);--k){ //枚举上一个子串的开头位置(上一个一定不能比这个长) // char a=x[k+lcp[k][p]],b=x[p+lcp[k][p]]; // if(k!=p-j||(lcp[k][p]<j&&a<b)) // dp[i][j]=(dp[i][j]+dp[p-1][p-k])%mod; // } dp[i][j]=sum[p-1][j-1]; //sum[i][j]表示符合当前【最后一个的长度小于j】的方法数(上一个比这个短当然较小) //考虑完上一个比这个短的情况,接下来只需要考虑长度相等的情况 int k=p-j; if(k<1) continue; char a=x[k+lcp[k][p]],b=x[p+lcp[k][p]]; if(lcp[k][p]<j&&a<b) dp[i][j]=(dp[i][j]+dp[p-1][j])%mod; } for(int j=1;j<=n;++j){ //这里注意是【<=n】而不是【<=i】,因为dp[i][j]=sum[p-1][j-1];会用到之前的状态(p-1),j-1可能大于p-1 sum[i][j]=(sum[i][j-1]+dp[i][j])%mod; } } ll s=0; for(int i=1;i<=n;++i){ s=(s+dp [i])%mod; } cout<<s<<endl; return 0; }
情况里可以出现前导零,即没有考虑到【要求分割后所有数字不得出现前导零】的代码(读漏题目的后果):
#include<bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; int lcp[5001][5001]; ll dp[5001][5001]; int main(){ int n; cin>>n; string x; cin>>x; x='0'+x; for(int i=n;i>=1;--i){ for(int j=n;j>i;--j){ if(x[i]==x[j]) lcp[i][j]=lcp[i+1][j+1]+1; } } for(int i=1;i<=n;++i) dp[0][i]=1; for(int i=1;i<=n;++i){ //这个子串的结尾位置 int w=i; for(int j=1;j<=i;++j){ //dp[i][j]:以【从x[i]往前j长度的子串】结尾的方法数 int p=i-j+1; //这个子串的开头位置 if(x[p]!='0') w=p; //这个子串的【非0】开头位置 int h=p-1; for(int k=p-1;k>=0;--k){ //枚举上一个子串的开头位置(上一个一定不能比这个长,错!比如0012,001比2长但比2小) if(x[k]!='0') h=k; //这个子串的【非0】开头位置 char a=x[h+lcp[h][w]],b=x[w+lcp[h][w]]; if(p-h<i-w+1||(p-h==i-w+1&&lcp[h][w]<j&&a<b)) //【这个串较长】或【一样长比较公共序列后第一个不同字符】 dp[i][j]=(dp[i][j]+dp[p-1][p-k])%mod; } } } ll s=0; for(int i=1;i<=n;++i){ s=(s+dp [i])%mod; } cout<<s<<endl; return 0; }
相关文章推荐
- 1834: [ZJOI2010]network 网络扩容
- 表格布局TableLayout ——实现商品浏览页面
- MD5函数
- POJ 1274 The Perfect Stall(二分图最大匹配)
- how to unlock /opt in ubuntu
- Centos环境下配置Aapache+2个tomcat8 的动静分离,负载均衡
- 为什么Float类型不能直接相等
- 【第一天】初步接触jQuery
- Cheese : A tool to use your camera
- [I0A]查找最大子串问题的求解
- macbook使用“终端”远程登录linux主机
- macbook使用“终端”远程登录linux主机
- 1031. Hello World for U (20)
- Light OJ 1169 - Monkeys on Twin Tower (简单DP)
- Python教程学习简记9--Python 返回函数 闭包
- Android批量图片加载经典系列——Volley框架实现多布局的新闻列表
- 面试笔试杂项积累-leetcode 241-245
- android 入门 005(登录记住)
- 关于Struts2三种访问Servlet API方式的总结
- 2016/2/12 codes