题面传送门

神仙题。

首先这个两次加密略微有点棘手,咱们不妨先从一次加密的情况入手考虑这个问题。显然,一次加密等价于将加密过的序列划分成若干段,每一段都是 \(xd\) 的形式表示这一段中有 \(x\) 个字符 \(d\)。那么我们就可以设 \(dp_{i}\) 表示原字符串长度为 \(i\) 的前缀可以由多少个字符串经过一次加密得到,转移就枚举上一段结尾 \(j(j\le i-2)\) 然后转移即可,只不过 \(j\) 可以转移到 \(i\) 需要满足两个条件:一是上一段的结尾与这一段的结尾不同,即 \(s_i\ne s_j\),二是这一段不能出现前导零,即 \(s_{j+1}\ne 0\)。

接下来考虑两次加密的情况,我们还是按照一次加密的情况枚举上一段的结尾 \(j\),这样第一次解密出来就是 \(x\) 个字符 \(d\),其中 \(x\) 是 \(s[j+1...i-1]\) 连接而成的 \(i-j-1\) 位数,\(d\) 是 \(s_j\) 表示的数。以 \(x=6,d=5\) 为例,第二次解密共有以下划分方法:

  • 直接跳过这 \(x\) 位数,或者说,这次解密出来的 \(6\) 个 \(5\) 完全被划分在同一段中,并且最后一个 \(5\) 不是这一段的结尾,比方说前面有 \(3\) 个 \(3\),后面有 \(2\) 个 \(7\),那么第二次解密出来的结果如下:\(3335555557\) 个 \(7\)
  • 上一段没有剩余的字符留下来,并且这段中间被断开,那么由于划分出来相邻两段的最后一个字符不能相同,故这 \(x\) 个 \(d\) 最多被切一刀(否则假设这两个断点分别为 \(i,j\),那么显然 \(s_i,s_j\) 为这两段的结尾,而由于 \(s_i=s_j\),不符合要求)。还是以 \(x=6,d=5\),前面有 \(3\) 个 \(3\),后面有 \(2\) 个 \(7\) 的情况为例,有以下 \(5\) 种划分方法:
    • \(333|55|555577\)
    • \(333|555|55577\)
    • \(333|5555|5577\)
    • \(333|55555|577\)
    • \(333|555555|77\)
  • 上一段有剩余的字符留下来,并且这段中间被断开,照样采用上面的例子,不妨设前面三个 \(3\) 在第二、三个 \(3\) 中间切了一道,那么有以下 \(6\) 种划分方式:
    • \(33|35|5555577\)
    • \(33|355|555577\)
    • \(33|3555|55577\)
    • \(33|35555|5577\)
    • \(33|355555|577\)
    • \(33|3555555|77\)

受到这个思想的启发,我们可以设 \(dp_{i,d,k}\) 表示当前解密了原字符串的前 \(i\) 位,在第一次解密出来的字符串中进行划分,划分出来最后一段的最后一位为 \(d\),当前第一次解密出来的字符串中是否有字符还没有划分为完整的一段的情况为 \(k\) 的方案数。转移还是枚举原字符串中上一段的右端点位置为 \(j\),上一段最后一个字符 \(p\),我们假设 \(s[j+1...i-1]\) 组成的数为 \(x\),\(s_i=d\),那么可以分以下情况:

  • 第一次解密出来的 \(x\) 个 \(d\) 中间没有划分,那么显然这 \(x\) 个 \(d\) 还没有被划分为完整的一段,故 \(dp_{i,p,1}\leftarrow dp_{j,p,1},dp_{i,p,1}\leftarrow dp_{j,p,0}\),当然如果 \(d=0\) 就不能从 \(dp_{j,p,0}\) 转移,因为这样会出现 \(pppp|000...0\) 的情况,就会出现前导零了。
  • 上一段没有剩余的字符留下来,并且这段中间被断开,那么共有 \(x-1\) 种可能,其中 \(x-2\) 种有字符剩余,\(1\) 种没有字符剩余,故 \(dp_{i,d,1}\leftarrow dp_{j,p,0}·(x-2),dp_{i,d,0}\leftarrow dp_{j,p,0}\),当然如果 \(d=0\) 或 \(d=p\) 就无法转移了,因为会出现前导零或者相邻两段结尾位置相同的情况,\(x=1\) 时无法转移到 \(dp_{i,d,1}\)。
  • 上一段有剩余的字符留下来,并且这段中间被断开,那么共有 \(x\) 种可能,其中 \(x-1\) 种有字符剩余,\(1\) 种没有字符剩余,故 \(dp_{i,d,1}\leftarrow dp_{j,p,1}·(x-1),dp_{i,d,0}\leftarrow dp_{j,p,1}\),同理如果 \(d=0\) 或 \(x=1\) 也无法转移到 \(dp_{i,d,1}\),因为划分出来下一段的第一个字符为 \(0\),不合法。

最终答案即为 \(dp_{n,s_n,0}\)。

时间复杂度 \(10n^2\)

const int MAXN=500;
const int MOD=1e9+9;
int n,dp[MAXN+5][11][2],pw10[MAXN+5];
struct StringDecryption{
int decrypt(vector<string> code){
string s;
for(int i=0;i<code.size();i++) s=s+code[i];
n=s.size();s=" "+s;dp[0][10][0]=pw10[0]=1;
for(int i=1;i<=n;i++) pw10[i]=10ll*pw10[i-1]%MOD;
for(int i=1;i<=n;i++){
int sum=0,dig=s[i]-'0';
for(int j=i-2;~j;j--){
sum=(sum+1ll*pw10[i-2-j]*(s[j+1]-'0'))%MOD;
if(s[j+1]=='0'||s[j]==s[i]) continue;
// printf("%d %d %d\n",i,j,sum);
for(int k=0;k<=10;k++){
if(dig!=0) dp[i][k][1]=(dp[i][k][1]+dp[j][k][0])%MOD;
dp[i][k][1]=(dp[i][k][1]+dp[j][k][1])%MOD;
if(dig!=k){
if(dig!=0){
if(!(j==i-2&&sum==1)) dp[i][dig][1]=(dp[i][dig][1]+1ll*(sum-2+MOD)*dp[j][k][0])%MOD;
dp[i][dig][1]=(dp[i][dig][1]+1ll*(sum-1+MOD)*dp[j][k][1])%MOD;
}
dp[i][dig][0]=(dp[i][dig][0]+dp[j][k][1])%MOD;
if(dig!=0&&!(j==i-2&&sum==1)) dp[i][dig][0]=(dp[i][dig][0]+dp[j][k][0])%MOD;
}
}
}
// printf("%d %d\n",dp[i][dig][0],dp[i][dig][1]);
} return dp[n][s[n]-'0'][0];
}
};

Topcoder 10748 - StringDecryption(dp)的更多相关文章

  1. LightOJ 1033 Generating Palindromes(dp)

    LightOJ 1033  Generating Palindromes(dp) 题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid= ...

  2. lightOJ 1047 Neighbor House (DP)

    lightOJ 1047   Neighbor House (DP) 题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=87730# ...

  3. UVA11125 - Arrange Some Marbles(dp)

    UVA11125 - Arrange Some Marbles(dp) option=com_onlinejudge&Itemid=8&category=24&page=sho ...

  4. 【POJ 3071】 Football(DP)

    [POJ 3071] Football(DP) Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 4350   Accepted ...

  5. 初探动态规划(DP)

    学习qzz的命名,来写一篇关于动态规划(dp)的入门博客. 动态规划应该算是一个入门oier的坑,动态规划的抽象即神奇之处,让很多萌新 萌比. 写这篇博客的目标,就是想要用一些容易理解的方式,讲解入门 ...

  6. Tour(dp)

    Tour(dp) 给定平面上n(n<=1000)个点的坐标(按照x递增的顺序),各点x坐标不同,且均为正整数.请设计一条路线,从最左边的点出发,走到最右边的点后再返回,要求除了最左点和最右点之外 ...

  7. 2017百度之星资格赛 1003:度度熊与邪恶大魔王(DP)

    .navbar-nav > li.active > a { background-image: none; background-color: #058; } .navbar-invers ...

  8. Leetcode之动态规划(DP)专题-详解983. 最低票价(Minimum Cost For Tickets)

    Leetcode之动态规划(DP)专题-983. 最低票价(Minimum Cost For Tickets) 在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行.在接下来的一年里,你要旅行的 ...

  9. 最长公共子序列长度(dp)

    /// 求两个字符串的最大公共子序列长度,最长公共子序列则并不要求连续,但要求前后顺序(dp) #include <bits/stdc++.h> using namespace std; ...

随机推荐

  1. SharkCTF2021 Babyhttp && get_or_lose

    两道web. Babyhttp: 直接dirsearch,发现同时存在git和bak泄露:经验证,git的没用. 访问index.php.bak, 下载源码: 抓包,改包,发包即可. get_or_l ...

  2. springcloud(二) 微服务架构编码构建

    微服务架构编码构建 1 基础知识 1.1 版本 2 微服务cloud整体聚合父工程Project 2.1 new project 2.2 字符编码设置 utf-8 2.3 pom.xml 2.4 父工 ...

  3. 认真讲说static关键字

    static 关键字主要有以下四种使用场景 修饰成员变量和成员方法 静态代码块 修饰类(只能修饰内部类) 静态导包(用来导入类中的静态资源,1.5之后的新特性) 修饰成员变量和成员方法(常用) 被 s ...

  4. Python网络爬虫实战入门

    一.网络爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人),是一种按照一定的规则,自动地抓取万维网信息的程序. 爬虫的基本流程: 发起请求: 通过HTTP库向目标站点发起请求,也就是发送一个Request ...

  5. 旋转数组的最小数字 牛客网 剑指Offer

    旋转数组的最小数字 牛客网 剑指Offer 题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4, ...

  6. binary-tree-maximum-path-sum leetcode C++

    Given a binary tree, find the maximum path sum. The path may start and end at any node in the tree. ...

  7. vsftpd 编译安装 及 隐藏版本号

    环境:Redhat Enterprise Linux AS 4.0 update2(i386) 不提示,均表示以root权限执行. [注:]//为注释符,如"// 建立MySQL组" ...

  8. RedHat 7.0 Linux 下划分区,分区加密,配额,逻辑卷管理

    1:如何划分区: 1:明确分区的对象:xxx :fdisk /dev/xxx 2:增加一个分区:n:选择主分区或者扩展分区,"p" or "e" :默认地方开始 ...

  9. k8s入坑之路(13)kubernetes重要资源(namespace隔离 resources资源管理 label)

    Namespace --- 集群的共享与隔离 语言中namespace概念 namespace核心作用隔离 以上是隔离的代码.namespace隔离的是: 1.资源对象的隔离:Service.Depl ...

  10. 『学了就忘』Linux基础命令 — 31、grep命令和通配符

    目录 1.grep命令介绍 2.find命令和grep命令的区别(重点) (1)find命令 (2)grep命令 3.通配符与正则表达式的区别 (1)通配符: (2)正则表达式: 1.grep命令介绍 ...