【bzoj3530】数数

Portal --> bzoj3530

Solution

  额其实只是因为。。这种在ac自动机上面拿节点编号当状态的dp方式挺好的但是好像忘得差不多了==导致今天想了一段时间。。所以还是丢上来提醒一下自己吧

  实际上一开始想到的是数位dp但是一看这个奇怪的限制以及这个数据范围那。。肯定没有前途

  所以我们考虑用一些字符串相关知识来处理

  多串匹配什么的。。ac自动机咯,所以我们可以先将\(m\)个数丢到ac自动机里面,然后考虑在上面dp:

  记\(f[i][j][0/1/2]\)表示当前这个数已经确定了前\(i\)位,在ac自动机上面的节点\(j\),这个确定的前\(i\)位抠出来组成的数与上限\(n\)的前\(i\)位抠出来组成的数的大小关系(\(0\sim 2\)三个数对应三种状态:等于小于大于),那接下来枚举第一维和第二维以及这一位填什么数,如果儿子为空那就一直跳fail就跟匹配一样,剩下的该怎么转移就怎么正常转就好了

  稍微要注意一下的是,因为前导零这种东西十分令人难受,所以我们对于所有的\(f[1][j][0/1/2]\)强行初始化一下。。另外就是转移的时候不能直接赋值而是应该一直累加(都一直跳fail了怎么可能只转移一次),最后就是感觉这种类型的转移的话。。转移有效状态那样会好很多(也就是看\(f[i][j][k]\)能够转到哪里去,并将\(f[i][j][k]\)的值加过去)

  

​  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define equal 0
#define smaller 1
#define bigger 2
using namespace std;
const int N=1210,M=110,L=1510,C=10,MOD=1e9+7;
char s[N];
int val[N];
int n,m,ans;
int plu(int x,int y){return (1LL*x+y)%MOD;}
namespace Ac{/*{{{*/
	const int N=::L;
	queue<int> q;
	int ch[N][C],fail[N],nok[N];
	int f[::N][N][3];
	int tot,rt;
	void init(){tot=0; rt=0;}
	void insert(char *s){
		int now=rt,len=strlen(s),c;
		for (int i=0;i<len;++i){
			c=s[i]-'0';
			if (ch[now][c]==0) ch[now][c]=++tot;
			now=ch[now][c];
		}
		nok[now]=1;
	}
	void get_fail(){
		int u,v;
		while (!q.empty()) q.pop();
		q.push(rt);
		while (!q.empty()){
			v=q.front(); q.pop();
			for (int i=0;i<C;++i){
				if (!ch[v][i]){
					ch[v][i]=ch[fail[v]][i];
					continue;
				}
				if (v!=rt){
					nok[ch[v][i]]|=nok[ch[fail[v]][i]];
					fail[ch[v][i]]=ch[fail[v]][i];
				}
				else
					fail[ch[v][i]]=rt;
				q.push(ch[v][i]);
			}
		}
	}
	int cmp(int x,int y){
		if (x==y) return equal;
		if (x<y) return smaller;
		if (x>y) return bigger;
	}
	void dp(){
		memset(f,0,sizeof(f));
		for (int i=1;i<=9;++i)
			if (!nok[ch[rt][i]]) 
				f[1][ch[rt][i]][cmp(i,val[1])]+=1;
		int x,st;
		for (int i=1;i<n;++i){
			for (int j=0;j<=tot;++j){
				if (!f[i][j][0]&&!f[i][j][1]&&!f[i][j][2]) continue;
				for (int k=0;k<C;++k){
					x=ch[j][k];
					if (nok[x]) continue;
					st=cmp(k,val[i+1]);
					if (st==equal){
						f[i+1][x][equal]=plu(f[i+1][x][equal],f[i][j][equal]);
						f[i+1][x][smaller]=plu(f[i+1][x][smaller],f[i][j][smaller]);
						f[i+1][x][bigger]=plu(f[i+1][x][bigger],f[i][j][bigger]);
					}
					else if (st==smaller){
						f[i+1][x][smaller]=plu(f[i+1][x][smaller],plu(f[i][j][smaller],f[i][j][equal]));
						f[i+1][x][bigger]=plu(f[i+1][x][bigger],f[i][j][bigger]);
					}
					else{//bigger
						f[i+1][x][smaller]=plu(f[i+1][x][smaller],f[i][j][smaller]);
						f[i+1][x][bigger]=plu(f[i+1][x][bigger],plu(f[i][j][equal],f[i][j][bigger]));
					}
				}
			}
		}
		ans=0;
		for (int i=1;i<=n;++i){
			for (int j=0;j<=tot;++j){
				ans=plu(ans,f[i][j][smaller]+f[i][j][equal]);
				if (i<n)
					ans=plu(ans,f[i][j][bigger]);
			}
		}
	}
}/*}}}*/

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	scanf("%s",s);
	n=strlen(s);
	for (int i=0;i<n;++i) val[i+1]=s[i]-'0';
	scanf("%d",&m);
	Ac::init();
	for (int i=1;i<=m;++i)
		scanf("%s",s),Ac::insert(s);
	Ac::get_fail();
	Ac::dp();
	printf("%d\n",ans);
}
posted @ 2018-10-23 15:42  yoyoball  阅读(121)  评论(0编辑  收藏  举报