传送门


如果\(r-l\)比较小,可以将所有满足条件的串扔进\(AC\)自动机然后在上面DP,从前往后确定字符串的每一位。

但是\(l,r \leq 10^{800}\)就十分不可行,所以需要优化这个算法。

考虑可能会有某一个节点的子节点连向的所有子节点构成一个满十叉树,意即当到达了这个节点之后可以随便往下走子节点,而且不论怎么走得到的结果都相同。比如说对于某一个九位数\(x = \overline {x_1x_2x_3x_4x_5x_6x_7x_8x_9}\),当前的\(l=123456789,r = 987654321\),那么若\(x_1 \in [2,8]\),无论\(x_2\)到\(x_9\)如何取值,\(x\)一定满足\(l \leq x \leq r\)。而当\(x_1 = 1,x2 \in [3,9]\)时都可以满足\(l \leq x \leq r\),无论\(x_3\)到\(x_9\)如何取值。

我们不妨把长度为\(k\)的“无论如何取值都能产生一个合法情况”的情况叫做产生一个\(k\)位通配符,比如说上面举的例子中\(x_1\in[2,8]\)时就会产生一个\(8\)位通配符,而\(x_1 = 1,x2 \in [3,9]\)则产生了一个\(7\)位通配符。特殊地,如果某个串刚好与\(l\)或\(r\)相等,我们认为产生了一个\(0\)位通配符(也就表示答案\(+1\))。

考虑如何会产生一个若干位的通配符。对于\(\geq l\)的情况,当\(\forall j \ x_j = l_j\)且\(x_{j+1} > l_{j+1}\),就会产生一个\(|l| - j - 1\)位的通配符,\(\leq r\)的情况类似,而如果\(|l| < |r|\),只要任意选择第一位为\(1\)到\(9\)的数,就可以产生\(|l| + 1\)到\(|r| - 1\)位的通配符。

可以发现最后产生的串的答案数量就是这个串中出现的通配符数量的总和。那么我们只需要计算出\(AC\)自动机上到达每一个节点能够产生的通配符数量,然后就可以比较快速地DP了。

将\(l,r\)两个串以及所有能够出现通配符情况的串放在一起构建Trie图/AC自动机,构建方法类似数位DP,对于每一个\(l\)和\(r\)的前缀枚举能够产生通配符的下一个字符来计算\(sum_{i,j}\)表示到达第\(i\)个节点能够获得的长度为\(j\)的通配符数量。注意通配符数量是可以通过\(fail\)指针传递的。具体细节请阅读下面代码中的insert函数和build函数

又设\(dp_{i,j}\)表示长度为\(i\)、当前所在节点为\(j\)的最大通配符数量,转移:\(dp_{i,j} + \sum\limits_{k=0}^{N-i-1}sum_{ch_{i , c} , k} \rightarrow dp_{i + 1 , ch_{i , c}}\)

后面的\(sum\)只需要枚举到\(N-i-1\)的原因是通配符的结尾不能超出字符串的结尾。

将\(sum\)前缀和,将复杂度做到\(O((|l| + |r|)NA^2)\),其中\(A\)为字符集大小。

输出方案倒推一边即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<vector>
#include<cmath>
#include<random>
//This code is written by Itst
using namespace std; int N;
struct node{
int ch[10] , sum[2007] , fail;
}Trie[16017];
int cnt = 1 , L1 , L2;
char L[807] , R[807]; #define get(x , y) (!Trie[x].ch[y] ? Trie[x].ch[y] = ++cnt : Trie[x].ch[y]) void insert(){
L1 = strlen(L + 1) , L2 = strlen(R + 1);
int u = 1 , v = 1;
if(L1 == L2){
for(int j = 1 ; j <= L1 ; ++j)
if(u == v){
for(int k = L[j] - '0' + 1 ; k < R[j] - '0' ; ++k)
++Trie[get(u , k)].sum[L1 - j];
u = get(u , L[j] - '0');
v = get(v , R[j] - '0');
}
else{
for(int k = L[j] - '0' + 1 ; k <= 9 ; ++k)
++Trie[get(u , k)].sum[L1 - j];
u = get(u , L[j] - '0');
for(int k = j == 1 ; k < R[j] - '0' ; ++k)
++Trie[get(v , k)].sum[L2 - j];
v = get(v , R[j] - '0');
}
++Trie[u].sum[0];Trie[v].sum[0] += u != v;
}
else{
for(int j = 1 ; j <= L1 ; ++j){
for(int k = L[j] - '0' + 1 ; k <= 9 ; ++k)
++Trie[get(u , k)].sum[L1 - j];
u = get(u , L[j] - '0');
}
for(int j = 1 ; j <= L2 ; ++j){
for(int k = j == 1 ; k < R[j] - '0' ; ++k)
++Trie[get(v , k)].sum[L2 - j];
v = get(v , R[j] - '0');
}
for(int j = L1 + 1 ; j < L2 ; ++j)
for(int k = 1 ; k <= 9 ; ++k)
++Trie[get(1 , k)].sum[j - 1];
++Trie[u].sum[0];++Trie[v].sum[0];
}
} void build(){
queue < int > q;
for(int i = 0 ; i < 10 ; ++i)
if(!Trie[1].ch[i])
Trie[1].ch[i] = 1;
else{
Trie[Trie[1].ch[i]].fail = 1;
q.push(Trie[1].ch[i]);
}
while(!q.empty()){
int t = q.front();
q.pop();
for(int j = 0 ; j < L2 ; ++j)
Trie[t].sum[j] += Trie[Trie[t].fail].sum[j];
for(int i = 0 ; i < 10 ; ++i)
if(!Trie[t].ch[i])
Trie[t].ch[i] = Trie[Trie[t].fail].ch[i];
else{
Trie[Trie[t].ch[i]].fail = Trie[Trie[t].fail].ch[i];
q.push(Trie[t].ch[i]);
}
}
for(int i = 1 ; i <= cnt ; ++i)
for(int j = 1 ; j < N ; ++j)
Trie[i].sum[j] += Trie[i].sum[j - 1];
} void init(){
scanf("%s %s %d" , L + 1 , R + 1 , &N);
insert();
build();
} int dp[2007][16017];
bool can[2007][16017]; inline int maxx(int a , int b){
return a > b ? a : b;
} int main(){
init();
memset(dp , -0x3f , sizeof(dp));
dp[0][1] = 0;
for(int i = 0 ; i < N ; ++i)
for(int j = 1 ; j <= cnt ; ++j)
if(dp[i][j] >= 0)
for(int k = 0 ; k < 10 ; ++k)
dp[i + 1][Trie[j].ch[k]] = maxx(dp[i + 1][Trie[j].ch[k]] , dp[i][j] + Trie[Trie[j].ch[k]].sum[N - i - 1]);
int ans = 0;
for(int i = 1 ; i <= cnt ; ++i)
ans = maxx(ans , dp[N][i]);
cout << ans << endl;
for(int i = 1 ; i <= cnt ; ++i)
can[N][i] = (dp[N][i] == ans);
for(int i = N - 1 ; i >= 0 ; --i)
for(int j = 1 ; j <= cnt ; ++j)
if(dp[i][j] >= 0)
for(int k = 0 ; !can[i][j] && k < 10 ; ++k)
can[i][j] = can[i + 1][Trie[j].ch[k]] && (dp[i + 1][Trie[j].ch[k]] == dp[i][j] + Trie[Trie[j].ch[k]].sum[N - i - 1]);
int u = 1;
for(int i = 1 ; i <= N ; ++i)
for(int j = 0 ; j < 10 ; ++j)
if(can[i][Trie[u].ch[j]] && (dp[i][Trie[u].ch[j]] == dp[i - 1][u] + Trie[Trie[u].ch[j]].sum[N - i])){
putchar(j + '0');
u = get(u , j);
break;
}
return 0;
}

CF1110H Modest Substrings AC自动机、DP的更多相关文章

  1. POJ1625 Censored!(AC自动机+DP)

    题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后 ...

  2. HDU2296 Ring(AC自动机+DP)

    题目是给几个带有价值的单词.而一个字符串的价值是 各单词在它里面出现次数*单词价值 的和,问长度不超过n的最大价值的字符串是什么? 依然是入门的AC自动机+DP题..不一样的是这题要输出具体方案,加个 ...

  3. HDU2457 DNA repair(AC自动机+DP)

    题目一串DNA最少需要修改几个基因使其不包含一些致病DNA片段. 这道题应该是AC自动机+DP的入门题了,有POJ2778基础不难写出来. dp[i][j]表示原DNA前i位(在AC自动机上转移i步) ...

  4. hdu 4117 GRE Words AC自动机DP

    题目:给出n个串,问最多能够选出多少个串,使得前面串是后面串的子串(按照输入顺序) 分析: 其实这题是这题SPOJ 7758. Growing Strings AC自动机DP的进阶版本,主题思想差不多 ...

  5. hdu 2457(ac自动机+dp)

    题意:容易理解... 分析:这是一道比较简单的ac自动机+dp的题了,直接上代码. 代码实现: #include<stdio.h> #include<string.h> #in ...

  6. HDU 2425 DNA repair (AC自动机+DP)

    DNA repair Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  7. HDU2296——Ring(AC自动机+DP)

    题意:输入N代表字符串长度,输入M代表喜欢的词语的个数,接下来是M个词语,然后是M个词语每个的价值.求字符串的最大价值.每个单词的价值就是单价*出现次数.单词可以重叠.如果不止一个答案,选择字典序最小 ...

  8. tyvj P1519 博彩游戏(AC自动机+DP滚动数组)

    P1519 博彩游戏 背景 Bob最近迷上了一个博彩游戏…… 描述 这个游戏的规则是这样的:每花一块钱可以得到一个随机数R,花上N块钱就可以得到一个随机序列:有M个序列,如果某个序列是产生的随机序列的 ...

  9. bzoj 1030 [JSOI2007]文本生成器(AC自动机+DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1030 [题意] 给n个小串,随机构造一个长为m的大串,一个串合法当且仅当包含一个或多个 ...

随机推荐

  1. drupal7常用函数

    1.获取当前启用的管理员主题名称: $admin_theme = variable_get('admin_theme');

  2. js 中三元运算符的运用

    外层为false,逐级向内层判断 $scope.nums=700; $scope.result=($scope.nums>300)?($scope.nums>400)?($scope.nu ...

  3. XHTML结构化

    XHTML 规则概要 将传统的 HTML 转换为 XHTML 1.0 是快捷且无痛的,只要你遵守一些简单的规则和容易的方针.不管是否使用过 HTML,都不会妨碍你使用 XHTML. 使用恰当的文档类型 ...

  4. Android学习笔记----天地图API开发之UnsatisfiedLinkError

    由于在jniLibs目录下移除了x86的相关so文件,后来又因为需要在PC模拟器上调试,将该文件夹恢复后,增加了天地图的sdk,却忘记将libMapEngine.so文件同时拷贝至x86目录下,导致如 ...

  5. 【转】解决configure: error: C++ compiler cannot create executables问题

    转自:http://www.coderbolg.com/content/83.html 啊……天啊,./configure时报错:configure: error: C++ compiler cann ...

  6. Linux 学习笔记之超详细基础linux命令 Part 11

    Linux学习笔记之超详细基础linux命令 by:授客 QQ:1033553122 ---------------------------------接Part 10---------------- ...

  7. 辽宁移动宽带体验及魔百盒M101s-2刷机

    一.背景 坐标:辽宁 某城,移动宽带100M. 设备:移动赠送,华为光猫一只,魔百盒M101s-2电视盒子 一只,据安装人员说这个魔百盒是移动自己开发设计的. 二.上网体验 上网:浏览一般网站没问题. ...

  8. 在Java中动态传参调用Python脚本

    最近,又接触到一个奇葩的接口,基于老板不断催促赶时间的情况下,在重写java接口和复用已有的python脚本的两条路中选择了后者,但是其实后者并没有好很多,因为我是一个对python的认识仅限于其名称 ...

  9. CentOS7安装搭建.Net Core 2.0环境-详细步骤

    一.构建.Net core 2的应用程web发布 因为是用来测试centos上的core 环境,先直接用vs17自带的core实例. 二.部署CentOS7的core环境 1.连接并启动之前安装的虚拟 ...

  10. Linux 小知识翻译 - 「虚拟化技术」

    这次聊聊「虚拟化技术」. 虚拟化技术,有时简称为「虚拟化」,最近经常听人说它.但是却不太清楚它的意思.到底虚拟了什么东西?本来是用来干什么的? 有名的虚拟化软件要数 VMware 和 VirtualB ...