题面传送门

神仙题。

首先这个两次加密略微有点棘手,咱们不妨先从一次加密的情况入手考虑这个问题。显然,一次加密等价于将加密过的序列划分成若干段,每一段都是 \(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. 【UE4 C++】 Config Settings配置文件(.ini)

    简介 常见存储路径 \Engine\Config\ \Engine\Saved\Config\ (运行后生成) [ProjectName]\Config\ [ProjectName]\Saved\Co ...

  2. Java:HashMap类小记

    Java:HashMap类小记 对 Java 中的 HashMap类,做一个微不足道的小小小小记 概述 HashMap:存储数据采用的哈希表结构,元素的存取顺序不能保证一致.由于要保证键的唯一.不重复 ...

  3. “介绍一下自己吧”——记2020BUAA软工团队介绍和采访

    写在前面 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任建) 这个作业的要求在哪里 团队作业-团队介绍和采访 团队介绍 团队名称 我们是 BUAA软软软件工程小队 ,简称 ...

  4. 热身训练2 Another Meaning

    题目来源 简要题意: 众所周知,在许多情况下,一个词语有两种意思.比如"hehe",不仅意味着"hehe",还意味着"excuse me". ...

  5. 2021.8.3考试总结[NOIP模拟29]

    T1 最长不下降子序列 数据范围$1e18$很不妙,但模数$d$只有$150$,考虑从这里突破. 计算的式子是个二次函数,结果只与上一个值有关,而模$d$情况下值最多只有$150$个,就证明序列会出现 ...

  6. 前端大牛带你了解JavaScript 函数式编程

    前言 函数式编程在前端已经成为了一个非常热门的话题.在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想. 本文将略去那些晦涩难懂的概念介绍,重点展示在 JavaScript 中到底 ...

  7. Go语言核心36讲(Go语言进阶技术十)--学习笔记

    16 | go语句及其执行规则(上) 我们已经知道,通道(也就是 channel)类型的值,可以被用来以通讯的方式共享数据.更具体地说,它一般被用来在不同的 goroutine 之间传递数据.那么 g ...

  8. 微软认真聆听了开源 .NET 开发社区的炮轰: 通过CLI 支持 Hot Reload 功能

    微软近日激怒了开源.NET社区,起因是它删除了开源.NET的一项旗舰功能,以提升Visual Studio 的吸引力,尤其是针对与Visual Studio颇有渊源的跨平台源代码编辑器Visual S ...

  9. ssh key公钥

    在ubuntu上生成ssh key 首先使用 ls -al ~/.ssh 查看本地是否已经有key 如果没有会显示如下: ~$ ls -al ~/.ssh ls: cannot access '/ho ...

  10. .NET 生态系统的蜕变之 .NET 6云原生

    云原生的英文名是cloud native,native 就是土著的意思,也就是土著对当地的环境是非常适应的,在云的环境和传统的数据中心是非常不同的,云原生就是要用的云的技术来构建应用, 利用云的技术来 ...