[CodeForces-585F]Digits of Number Pi

题目大意:
  给你一个数字串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很粗糙地实现了结点的遍历。故常数巨大。 

  1 #pragma GCC optimize("O3") 
  2 #include<queue>
  3 #include<cstdio>
  4 #include<vector>
  5 #include<cstring>
  6 const int mod=1e9+7;
  7 const int N=1001,D=51;
  8 char s[N],l[D],r[D];
  9 int n,d;
 10 class AhoCorasickAutomaton {
 11     private:
 12         static const int SIGMA_SIZE=10;
 13         struct Node {
 14             Node *ch[SIGMA_SIZE],*fail;
 15             bool isEnd;
 16             int id;
 17             Node(const int i) {
 18                 memset(ch,0,sizeof ch);
 19                 fail=NULL;
 20                 isEnd=false;
 21                 id=i;
 22             }
 23         };
 24         Node *root;
 25         std::vector<Node*> v; 
 26         int idx(const char ch) {
 27             return ch-'0';
 28         }
 29         int f[4][N*D>>1],g[4][N*D>>1];
 30         //第一维表示与l和r的关系
 31     public:
 32         AhoCorasickAutomaton() {
 33             root=new Node(v.size());
 34             v.push_back(root);
 35         }
 36         void insert(char s[],const int len) {
 37             Node *p=root;
 38             for(int i=0;i<len;i++) {
 39                 const int w=idx(s[i]);
 40                 if(!p->ch[w]) {
 41                     p->ch[w]=new Node(v.size());
 42                     v.push_back(p->ch[w]);
 43                 }
 44                 p=p->ch[w];
 45             }
 46             p->isEnd=true;
 47         }
 48         void getFail() {
 49             std::queue<Node*> q;
 50             root->fail=root;
 51             for(int i=0;i<SIGMA_SIZE;i++) {
 52                 if(root->ch[i]) {
 53                     root->ch[i]->fail=root;
 54                     q.push(root->ch[i]);
 55                 } else {
 56                     root->ch[i]=root;
 57                 }
 58             }
 59             while(!q.empty()) {
 60                 Node *p=q.front();
 61                 q.pop();
 62                 for(int i=0;i<SIGMA_SIZE;i++) {
 63                     if(p->ch[i]) {
 64                         p->ch[i]->fail=p->fail->ch[i];
 65                         q.push(p->ch[i]);
 66                     } else {
 67                         p->ch[i]=p->fail->ch[i];
 68                     }
 69                 }
 70             }
 71             Node *end=new Node(v.size());
 72             for(unsigned i=0;i<v.size();i++) {
 73                 Node *p=v[i];
 74                 for(int i=0;i<SIGMA_SIZE;i++) {
 75                     if(p->ch[i]->isEnd) {
 76                         p->ch[i]=end;
 77                     }
 78                 }
 79             }
 80             for(int i=0;i<SIGMA_SIZE;i++) {
 81                 end->ch[i]=end;
 82             }
 83             v.push_back(end);
 84         }
 85         int dp() {
 86             g[0][0]=1;
 87             int to[10];
 88             for(int i=0;i<d;i++) {
 89                 for(int i=0;i<4;i++) {
 90                     for(unsigned j=0;j<v.size();j++) {
 91                         f[i][j]=0;
 92                     }
 93                 }
 94                 for(int j=0;j<4;j++) {
 95                     int st=(j&1)?0:idx(l[i]),en=(j>1)?9:idx(r[i]);//确定当前数位数字的上下界 
 96                     for(int i=st;i<=en;i++) to[i]=0b11;//默认是在l和r之间 
 97                     if(~j&1) to[st]&=0b10;//如果比l小 
 98                     if(j<2) to[en]&=0b01;//如果比r大
 99                     //用&是因为有可能st=en 
100                     for(unsigned k=0;k<v.size();k++) {
101                         if(!g[j][k]) continue;
102                         for(int o=st;o<=en;o++) {//在当前数位的范围寻找子结点 
103                             (f[to[o]][v[k]->ch[o]->id]+=g[j][k])%=mod;
104                         }
105                     }
106                 }
107                 std::swap(f,g);
108             }
109             int ret=0;
110             for(int i=0;i<4;i++) {
111                 ret=(ret+g[i][v.size()-1])%mod;
112             }
113             return ret;
114         }
115 };
116 AhoCorasickAutomaton acam;
117 int main() {
118     scanf("%s%s%s",s,l,r);
119     n=strlen(s),d=strlen(l);
120     for(int i=0;i<=n-d/2;i++) {
121         acam.insert(&s[i],d/2);
122     }
123     acam.getFail();
124     printf("%d\n",acam.dp());
125     return 0;
126 }

 

posted @ 2017-09-19 10:05  skylee03  阅读(139)  评论(0编辑  收藏  举报