题目大意:
  给你一个数字串s,一个序列范围l和r,(l和r的数字位数为d)求l到r中有多少个数,满足它的长度为d/2的子串,能够在s中被匹配。

思路:
  首先将s中每一个长度为d/2的子串插入后缀自动机。
  然后数位DP。
  f[i][j]中第一维表示当前树与l和r的关系,包含四个状态,用二进制表示,每一位对应与l和r的不同关系。
  第二维表示当前状态下每个结点匹配到的数的个数。
  每一个数位的状态由上一个数位转移而来,我们用两个DP数组f和g滚动实现。
  用o表示当前枚举的数字,用to表示数字所对应的第一维的状态,则转移方程为f[to[o]][p]=sum(f[j][par[p]])
  然而一开始写AC自动机用的是指针,然后又是各种不方便,所以又用vector很粗糙地实现了结点的遍历。故常数巨大。

 #pragma GCC optimize("O3")
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
const int mod=1e9+;
const int N=,D=;
char s[N],l[D],r[D];
int n,d;
class AhoCorasickAutomaton {
private:
static const int SIGMA_SIZE=;
struct Node {
Node *ch[SIGMA_SIZE],*fail;
bool isEnd;
int id;
Node(const int i) {
memset(ch,,sizeof ch);
fail=NULL;
isEnd=false;
id=i;
}
};
Node *root;
std::vector<Node*> v;
int idx(const char ch) {
return ch-'';
}
int f[][N*D>>],g[][N*D>>];
//第一维表示与l和r的关系
public:
AhoCorasickAutomaton() {
root=new Node(v.size());
v.push_back(root);
}
void insert(char s[],const int len) {
Node *p=root;
for(int i=;i<len;i++) {
const int w=idx(s[i]);
if(!p->ch[w]) {
p->ch[w]=new Node(v.size());
v.push_back(p->ch[w]);
}
p=p->ch[w];
}
p->isEnd=true;
}
void getFail() {
std::queue<Node*> q;
root->fail=root;
for(int i=;i<SIGMA_SIZE;i++) {
if(root->ch[i]) {
root->ch[i]->fail=root;
q.push(root->ch[i]);
} else {
root->ch[i]=root;
}
}
while(!q.empty()) {
Node *p=q.front();
q.pop();
for(int i=;i<SIGMA_SIZE;i++) {
if(p->ch[i]) {
p->ch[i]->fail=p->fail->ch[i];
q.push(p->ch[i]);
} else {
p->ch[i]=p->fail->ch[i];
}
}
}
Node *end=new Node(v.size());
for(unsigned i=;i<v.size();i++) {
Node *p=v[i];
for(int i=;i<SIGMA_SIZE;i++) {
if(p->ch[i]->isEnd) {
p->ch[i]=end;
}
}
}
for(int i=;i<SIGMA_SIZE;i++) {
end->ch[i]=end;
}
v.push_back(end);
}
int dp() {
g[][]=;
int to[];
for(int i=;i<d;i++) {
for(int i=;i<;i++) {
for(unsigned j=;j<v.size();j++) {
f[i][j]=;
}
}
for(int j=;j<;j++) {
int st=(j&)?:idx(l[i]),en=(j>)?:idx(r[i]);//确定当前数位数字的上下界
for(int i=st;i<=en;i++) to[i]=0b11;//默认是在l和r之间
if(~j&) to[st]&=0b10;//如果比l小
if(j<) to[en]&=0b01;//如果比r大
//用&是因为有可能st=en
for(unsigned k=;k<v.size();k++) {
if(!g[j][k]) continue;
for(int o=st;o<=en;o++) {//在当前数位的范围寻找子结点
(f[to[o]][v[k]->ch[o]->id]+=g[j][k])%=mod;
}
}
}
std::swap(f,g);
}
int ret=;
for(int i=;i<;i++) {
ret=(ret+g[i][v.size()-])%mod;
}
return ret;
}
};
AhoCorasickAutomaton acam;
int main() {
scanf("%s%s%s",s,l,r);
n=strlen(s),d=strlen(l);
for(int i=;i<=n-d/;i++) {
acam.insert(&s[i],d/);
}
acam.getFail();
printf("%d\n",acam.dp());
return ;
}

[CodeForces-585F]Digits of Number Pi的更多相关文章

  1. CF585F Digits of Number Pi

    题目 把\(s\)串所有长度为\(\lfloor \frac{d}{2}\rfloor\)的子串插入一个ACAM中,之后数位dp就好了,状态是\(dp_{i,j,0/1}\)第\(i\)位,在ACAM ...

  2. 题解 CF585F 【Digits of Number Pi】

    考虑用数位 \(DP\) 来统计数字串个数,用 \(SAM\) 来实现子串的匹配. 设状态 \(f(pos,cur,lenth,lim,flag)\),表示数位的位数,在 \(SAM\) 上的节点,匹 ...

  3. codeforces 464C. Substitutes in Number

    题目链接 C. Substitutes in Number time limit per test 1 second memory limit per test 256 megabytes input ...

  4. 【codeforces 805D】Minimum number of steps

    [题目链接]:http://codeforces.com/contest/805/problem/D [题意] 给你一个字符串; 里面只包括a和b; 让你把里面的"ab"子串全都去 ...

  5. Codeforces C. Split a Number(贪心大数运算)

    题目描述: time limit per test 2 seconds memory limit per test 512 megabytes input standard input output ...

  6. dp --- Codeforces 245H :Queries for Number of Palindromes

    Queries for Number of Palindromes Problem's Link:   http://codeforces.com/problemset/problem/245/H M ...

  7. Codeforces 279D The Minimum Number of Variables 状压dp

    The Minimum Number of Variables 我们定义dp[ i ][ mask ]表示是否存在 处理完前 i 个a, b中存者 a存在的状态是mask 的情况. 然后用sosdp处 ...

  8. Educational Codeforces Round 11 D. Number of Parallelograms 暴力

    D. Number of Parallelograms 题目连接: http://www.codeforces.com/contest/660/problem/D Description You ar ...

  9. Codeforces 980 E. The Number Games

    \(>Codeforces \space 980 E. The Number Games<\) 题目大意 : 有一棵点数为 \(n\) 的数,第 \(i\) 个点的点权是 \(2^i\) ...

随机推荐

  1. .NET中如何自定义配置节点

    .NET Framework在web.config或app.config中默认提供了很多种设置,以便能够改变应用程序内嵌组件的行为,例如<connectionStrings>.<ht ...

  2. 一个diff工具,用于判断两个目录下所有的改动(比较新旧版本文件夹)

    需求: 编写一个diff工具,用于判断两个目录下所有的改动 详细介绍: 有A和B两个目录,目录所在位置及层级均不确定 需要以B为基准找出两个目录中所有有改动的文件(文件或内容增加.修改.删除),将有改 ...

  3. Hibernate5笔记2--单表的增删改查操作

    单表的增删改查操作: (1)定义获取Session和SessionFactory的工具类: package com.tongji.utils; import org.hibernate.Session ...

  4. 生成Word/ATU报表提示 font family not found

    1.先从你本机 C:\Windows\Fonts 拷贝或者网络上下载你想要安装的字体文件(*.ttf文件)到 /usr/share/fonts/chinese/TrueType 目录下(如果系统中没有 ...

  5. 编译安装 zbar 时两次 make 带来的惊喜

    为了装 php 的条形码扩展模块 php-zbarcode,先装了一天的 ImageMagick 和 zbar.也许和我装的 Ubuntu 17.10 的有版本兼容问题吧,总之什么毛病都有,apt 不 ...

  6. MyEclipse开发工具,当选中一个单词时,其他相同的单词会被高亮显示(选中/标记)

    1.步骤: Window-->Preferences-->Java-->Editor-->Mark Occurremces下的 Mark Occurremces of the ...

  7. HDU 2819 Swap(行列式性质+最大匹配)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2819 题目大意:给你一个n*n的01矩阵,问是否可以通过任意交换整行或者整列使得正对角线上都是1. ...

  8. es6之yield

    yield 关键字用来暂停和继续一个生成器函数.我们可以在需要的时候控制函数的运行. yield 关键字使生成器函数暂停执行,并返回跟在它后面的表达式的当前值.与return类似,但是可以使用next ...

  9. linux 系统网卡无法识别,缺少驱动

    #linux网卡驱动安装# Linux设备加载 #lsmod Module Size Used by e1000e 查看硬件设备 ls /usr/share/hwdata 查看pci网卡设备 lspc ...

  10. 经典面试题:n个数字(0,1,…,n-1)形成一个圆圈

    题目: n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始, 每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字). 当一个数字删除后,从被删除数字的下一个继续删 ...