题意:
数字都要转为
码,题目会给出
个长度不超过
的
限制串,然后给出
,求
区间内有多少个数的
码不包括限制串(
)。有
组数据。
这题卡了我几天,我这是用自动机
数位
解的,这题对
自动机
数位
的应用都不深,都是简单的应用,但
自动机我之前没学明白(写了一些题还是没明白),花几天又学了一遍,对
自动机的结构更清楚了,找出了紫书模板有的部分是多余的,给自己重学一遍时理解代码思路增加了一些困难,也发现当初对模板的注释有些地方含糊不清(自己都看不懂,当时写注释的时候就是在强行解释),所以对模板以及注释都做了修改。
debug那些憨憨事:
用按字节初始化时,我用了上次计算留下的变量
,但我在用这个变量之前把它初始化了,于是整个数组并没有初始化;没有特判前导零,前导零也加进去跑了一下,答案直接小了好多;
,在
串上跑十进制数的时候,我写的是
;
思路:
1.将所有限制串做成一个字典树
2.求出这个字典树对应的
数组(就是
数组),同时标记限制串的结尾,如果这个节点是某个限制串的结尾(即它的
指针指向的节点是某个限制串的结尾,或者它就是某个限制串的结尾),那么标记这个节点不能取。如果这个节点之前有节点被标记了,这个节点也要被标记。操作如下:
val[v]|=val[f[v]]|val[u];
3.将每一个被标记点的数组赋值成
(抹去这个节点),表示这个节点不能取,取了就会有限制串
if(val[ch[i][0]]) ch[i][0]=-1; if(val[ch[i][1]]) ch[i][1]=-1;
4.前面得到的数组都是在
串上跑出来的,而为了方便后面搜索答案,需要将再用
进制跑一遍
串上的
数组,得到一个数组可以叫它转移矩阵
5.一个进制数对应的
码有四位,也就是一个十进制数对应四个节点,可以通过位运算处理:
for(j=0; j<=9; ++j) {
for(now=i,k=8; k&&now!=-1; k>>=1)
now=ch[now][(j&k)!=0];//(j&k)!=0判断二进制位是0还是1
num[i][j]=now;
}//i是上一个状态,j是十进制数准备工作都做好了,下面开始数位
1.,表示当前节点状态为
,当前是否是前缀零,还可以走
步有多少个合法的数
2.都是大数,得字符串读入,所以最高位下标最小,不做处理直接搜索,不符合平时数位
的习惯(得到的
状态对搜索的数据有依赖),所以通过
这样的处理使得搜索时
可以从大到小,这样求完
的结果后,得到的状态可以用于求
3.每次输入的限制串都是不同的,所以上次计算的得到的状态不能用于求下一次计算,所以每次都需要初始化状态数组(这里我是另开一个数组标记这个状态是不是本次计算得到的,牺牲空间换时间)
4.最后再单独在上跑一遍字典树,如果出现了限制串,那么直接输出答案,否则说明多减了,
也是符合要求的
注意:
1.如果当前位是前导零,那么继续往后搜索时有的点取不了,得到的结果可能会比不含前导零的少,所以状态数组多开了一维判断当前是不是前导零。
2.是唯一的
3.按字节初始化
4.前导零特判,如果是前导零就跳过,不要走过去了,前导零会对结果造成影响。
Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2003,mod=1000000009;
int n,m,sz,cas,len;
int ch[maxn][2],f[maxn],last[maxn],num[maxn][10];
int dp[maxn][202][2],lazy[maxn][202][2];
bool val[maxn];
char s[233];
inline void insert() {
int u=0;
for(int i=0,c; s[i]; i++) {
c=s[i]-'0';
u=ch[u][c]?ch[u][c]:(ch[u][c]=++sz);
}
val[u]=true;
}
inline void build() {
queue<int>q;
for(int c=0,u; c<=1; c++) {
u = ch[0][c];
if(u) {
f[u]=0;
last[u] = 0;
q.push(u);
}
}
int u,v,c;
while(!q.empty()) {
u=q.front();
q.pop();
for(c=0; c<=1; c++) {
v=ch[u][c];
if(!v) {
ch[u][c]=ch[f[u]][c];
continue;
}
q.push(v);
f[v] = ch[f[u]][c];
val[v]|=val[f[v]]|val[u];
}
}
for(int i=0; i<=sz; ++i) {
if(val[ch[i][0]]) ch[i][0]=-1;
if(val[ch[i][1]]) ch[i][1]=-1;
}
for(int i=0,j,k,now; i<=sz; ++i) if(!val[i]) {
for(j=0; j<=9; ++j) {
for(now=i,k=8; k&&now!=-1; k>>=1)
now=ch[now][(j&k)!=0];
num[i][j]=now;
}
}
}
int dfs(int pos,int step,bool zero,bool limit) {
if(!step) return !zero;
if(!limit&&lazy[pos][step][zero]==cas) return dp[pos][step][zero];
int endi=(limit?(s[len-step+1]-'0'):9),ans=0;
if(zero) ans=dfs(pos,step-1,zero,limit&&0==endi);
else if(num[pos][0]!=-1) ans=dfs(num[pos][0],step-1,zero,limit&&0==endi);
for(int i=1; i<=endi; ++i) if(num[pos][i]!=-1) {
ans+=dfs(num[pos][i],step-1,zero&&i==0,limit&&i==endi);
if(ans>mod) ans-=mod;
}
if(!limit) {
dp[pos][step][zero]=ans;
lazy[pos][step][zero]=cas;
}
return ans;
}
int main() {
int ans,now;
for(scanf("%d",&cas); cas; --cas) {
scanf("%d",&n);
memset(val,false,sz+1);
memset(ch,0,(sz+1)<<3);
sz=0;
for(int i=0; i<n; ++i) {
scanf("%s",s);
insert();
}
build();
scanf("%s",s+1);
len=strlen(s+1);
now=num[0][s[1]-'0'];
for(int i=2; i<=len&&now!=-1; ++i) now=num[now][s[i]-'0'];
ans=(now!=-1)-dfs(0,len,true,true);
scanf("%s",s+1);
len=strlen(s+1);
ans+=dfs(0,len,true,true);
if(ans<0) ans+=mod;
printf("%d\n",ans);
}
return 0;
}
京公网安备 11010502036488号